From 0c1efe7420cde41cd762927868792517664f5fa1 Mon Sep 17 00:00:00 2001 From: hevinci Date: Tue, 18 Jul 2023 14:27:33 +0800 Subject: [PATCH] update runtime code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 新增WebGL专属模式WebPlayMode --- .../Runtime/AssetSystem/AssetSystem.cs | 16 +- .../Loader/AssetBundleFileLoader.cs | 10 +- .../Loader/AssetBundleWebLoader.cs | 189 +++------------ .../AssetSystem/Loader/BundleLoaderBase.cs | 22 +- .../AssetSystem/Loader/RawBundleFileLoader.cs | 6 +- .../AssetSystem/Loader/RawBundleWebLoader.cs | 11 +- .../AssetSystem/Provider/ProviderBase.cs | 18 +- .../Runtime/DownloadSystem/DownloadSystem.cs | 41 +++- .../Downloader/CompletedDownloader.cs | 23 ++ ...er.cs.meta => CompletedDownloader.cs.meta} | 0 .../Downloader/DownloaderBase.cs | 142 +++++++---- .../Downloader/FileGeneralDownloader.cs | 225 ++++++++++++++++++ ....cs.meta => FileGeneralDownloader.cs.meta} | 0 ...eDownloader.cs => FileResumeDownloader.cs} | 141 ++++------- .../Downloader/FileResumeDownloader.cs.meta | 11 + .../Downloader/TempDownloader.cs | 20 -- .../Downloader/WebDownloader.cs | 209 ++++++++++++++++ .../Downloader/WebDownloader.cs.meta | 11 + .../DownloadSystem/UnityWebDataRequester.cs | 82 +------ .../DownloadSystem/UnityWebFileRequester.cs | 114 +-------- .../DownloadSystem/UnityWebRequesterBase.cs | 116 +++++++++ .../UnityWebRequesterBase.cs.meta | 11 + .../YooAsset/Runtime/InitializeParameters.cs | 21 ++ .../Runtime/PackageSystem/ManifestTools.cs | 4 +- .../Operations/DownloaderOperation.cs | 11 +- .../Operations/InitializationOperation.cs | 87 +++++++ .../Internal/DeserializeManifestOperation.cs | 1 + .../Internal/LoadRemoteManifestOperation.cs | 150 ++++++++++++ .../LoadRemoteManifestOperation.cs.meta | 11 + .../QueryRemotePackageHashOperation.cs | 99 ++++++++ .../QueryRemotePackageHashOperation.cs.meta | 11 + .../QueryRemotePackageVersionOperation.cs | 4 +- .../Operations/PreDownloadContentOperation.cs | 10 + .../UpdatePackageManifestOperation.cs | 80 ++++++- .../UpdatePackageVersionOperation.cs | 62 +++++ .../Runtime/PackageSystem/PackageBundle.cs | 5 + .../PackageSystem/PlayMode/WebPlayModeImpl.cs | 157 ++++++++++++ .../PlayMode/WebPlayModeImpl.cs.meta | 11 + .../Runtime/PackageSystem/ResourcePackage.cs | 26 ++ .../Runtime/Settings/YooAssetSettings.cs | 2 +- 40 files changed, 1616 insertions(+), 554 deletions(-) create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs rename Assets/YooAsset/Runtime/DownloadSystem/Downloader/{TempDownloader.cs.meta => CompletedDownloader.cs.meta} (100%) create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs rename Assets/YooAsset/Runtime/DownloadSystem/Downloader/{FileDownloader.cs.meta => FileGeneralDownloader.cs.meta} (100%) rename Assets/YooAsset/Runtime/DownloadSystem/Downloader/{FileDownloader.cs => FileResumeDownloader.cs} (61%) create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs.meta delete mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs.meta create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs.meta create mode 100644 Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs create mode 100644 Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs.meta create mode 100644 Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs create mode 100644 Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs.meta create mode 100644 Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs create mode 100644 Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs.meta diff --git a/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs index 0f295ae..a44c3b0 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/AssetSystem.cs @@ -111,7 +111,7 @@ namespace YooAsset if (loader.CanDestroy()) { string bundleName = loader.MainBundleInfo.Bundle.BundleName; - loader.Destroy(false); + loader.Destroy(); _loaderList.RemoveAt(i); _loaderDic.Remove(bundleName); } @@ -123,15 +123,16 @@ namespace YooAsset /// public void ForceUnloadAllAssets() { +#if UNITY_WEBGL + throw new Exception($"WebGL not support invoke {nameof(ForceUnloadAllAssets)}"); +#else foreach (var provider in _providerList) { - provider.WaitForAsyncComplete(); - provider.Destroy(); + provider.DestroySafely(); } foreach (var loader in _loaderList) { - loader.WaitForAsyncComplete(); - loader.Destroy(true); + loader.DestroySafely(); } _providerList.Clear(); @@ -142,6 +143,7 @@ namespace YooAsset // 注意:调用底层接口释放所有资源 Resources.UnloadUnusedAssets(); +#endif } /// @@ -414,7 +416,7 @@ namespace YooAsset return null; } - #region 调试信息 +#region 调试信息 internal List GetDebugReportInfos() { List result = new List(_providerList.Count); @@ -442,6 +444,6 @@ namespace YooAsset } return result; } - #endregion +#endregion } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleFileLoader.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleFileLoader.cs index 85f8687..483eb97 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleFileLoader.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleFileLoader.cs @@ -82,7 +82,8 @@ namespace YooAsset if (_steps == ESteps.Download) { int failedTryAgain = Impl.DownloadFailedTryAgain; - _downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); + _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain); + _downloader.SendRequest(); _steps = ESteps.CheckDownload; } @@ -112,7 +113,8 @@ namespace YooAsset { int failedTryAgain = Impl.DownloadFailedTryAgain; var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); - _unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); + _unpacker = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain); + _unpacker.SendRequest(); _steps = ESteps.CheckUnpack; } @@ -262,9 +264,9 @@ namespace YooAsset /// /// 销毁 /// - public override void Destroy(bool forceDestroy) + public override void Destroy() { - base.Destroy(forceDestroy); + base.Destroy(); if (_stream != null) { diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleWebLoader.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleWebLoader.cs index 3b81a8f..fe81b44 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleWebLoader.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/AssetBundleWebLoader.cs @@ -15,21 +15,14 @@ namespace YooAsset private enum ESteps { None = 0, - Download, - CheckDownload, - LoadCacheFile, - CheckLoadCacheFile, - LoadWebFile, - CheckLoadWebFile, - TryLoadWebFile, + LoadWebSiteFile, + LoadRemoteFile, + CheckLoadFile, Done, } private ESteps _steps = ESteps.None; - private float _tryTimer = 0; - private DownloaderBase _downloader; - private UnityWebRequest _webRequest; - private AssetBundleCreateRequest _createRequest; + private WebDownloader _downloader; public AssetBundleWebLoader(AssetSystemImpl impl, BundleInfo bundleInfo) : base(impl, bundleInfo) @@ -48,18 +41,13 @@ namespace YooAsset { if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote) { - _steps = ESteps.Download; - FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; + _steps = ESteps.LoadRemoteFile; + FileLoadPath = string.Empty; } else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming) { - _steps = ESteps.LoadWebFile; - FileLoadPath = MainBundleInfo.Bundle.StreamingFilePath; - } - else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache) - { - _steps = ESteps.LoadCacheFile; - FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; + _steps = ESteps.LoadWebSiteFile; + FileLoadPath = string.Empty; } else { @@ -67,168 +55,49 @@ namespace YooAsset } } - // 1. 从服务器下载 - if (_steps == ESteps.Download) + // 1. 跨域获取资源包 + if (_steps == ESteps.LoadRemoteFile) { int failedTryAgain = Impl.DownloadFailedTryAgain; - _downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); - _steps = ESteps.CheckDownload; + _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain) as WebDownloader; + _downloader.SendRequest(true); + _steps = ESteps.CheckLoadFile; } - // 2. 检测服务器下载结果 - if (_steps == ESteps.CheckDownload) + // 2. 从站点获取资源包 + if (_steps == ESteps.LoadWebSiteFile) + { + int failedTryAgain = Impl.DownloadFailedTryAgain; + var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); + _downloader = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain) as WebDownloader; + _downloader.SendRequest(true); + _steps = ESteps.CheckLoadFile; + } + + // 3. 检测加载结果 + if (_steps == ESteps.CheckLoadFile) { DownloadProgress = _downloader.DownloadProgress; DownloadedBytes = _downloader.DownloadedBytes; if (_downloader.IsDone() == false) return; - if (_downloader.HasError()) - { - _steps = ESteps.Done; - Status = EStatus.Failed; - LastError = _downloader.GetLastError(); - } - else - { - _steps = ESteps.LoadCacheFile; - return; //下载完毕等待一帧再去加载! - } - } - - // 3. 从本地缓存里加载AssetBundle - if (_steps == ESteps.LoadCacheFile) - { -#if UNITY_EDITOR - // 注意:Unity2017.4编辑器模式下,如果AssetBundle文件不存在会导致编辑器崩溃,这里做了预判。 - if (System.IO.File.Exists(FileLoadPath) == false) - { - _steps = ESteps.Done; - Status = EStatus.Failed; - LastError = $"Not found assetBundle file : {FileLoadPath}"; - YooLogger.Error(LastError); - return; - } -#endif - - // 设置下载进度 - DownloadProgress = 1f; - DownloadedBytes = (ulong)MainBundleInfo.Bundle.FileSize; - - // Load assetBundle file - var loadMethod = (EBundleLoadMethod)MainBundleInfo.Bundle.LoadMethod; - if (loadMethod == EBundleLoadMethod.Normal) - { - _createRequest = AssetBundle.LoadFromFileAsync(FileLoadPath); - } - else - { - _steps = ESteps.Done; - Status = EStatus.Failed; - LastError = $"WebGL not support encrypted bundle file : {MainBundleInfo.Bundle.BundleName}"; - YooLogger.Error(LastError); - return; - } - _steps = ESteps.CheckLoadCacheFile; - } - - // 4. 检测AssetBundle加载结果 - if (_steps == ESteps.CheckLoadCacheFile) - { - if (_createRequest.isDone == false) - return; - - CacheBundle = _createRequest.assetBundle; + CacheBundle = _downloader.GetAssetBundle(); if (CacheBundle == null) { _steps = ESteps.Done; Status = EStatus.Failed; - LastError = $"Failed to load AssetBundle file : {MainBundleInfo.Bundle.BundleName}"; + LastError = $"AssetBundle file is invalid : {MainBundleInfo.Bundle.BundleName}"; YooLogger.Error(LastError); - - // 注意:当缓存文件的校验等级为Low的时候,并不能保证缓存文件的完整性。 - // 在AssetBundle文件加载失败的情况下,我们需要重新验证文件的完整性! - if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache) - { - var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID); - if (result != EVerifyResult.Succeed) - { - YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}"); - CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID); - } - } } else { _steps = ESteps.Done; Status = EStatus.Succeed; } - } - // 5. 从WEB网站获取AssetBundle文件 - if (_steps == ESteps.LoadWebFile) - { - if (CacheSystem.DisableUnityCacheOnWebGL) - { - _webRequest = UnityWebRequestAssetBundle.GetAssetBundle(FileLoadPath); - } - else - { - var hash = Hash128.Parse(MainBundleInfo.Bundle.FileHash); - _webRequest = UnityWebRequestAssetBundle.GetAssetBundle(FileLoadPath, hash); - } - - DownloadSystem.SetUnityWebRequest(_webRequest); - _webRequest.SendWebRequest(); - _steps = ESteps.CheckLoadWebFile; - } - - // 6. 检测AssetBundle加载结果 - if (_steps == ESteps.CheckLoadWebFile) - { - DownloadProgress = _webRequest.downloadProgress; - DownloadedBytes = _webRequest.downloadedBytes; - if (_webRequest.isDone == false) - return; - -#if UNITY_2020_1_OR_NEWER - if (_webRequest.result != UnityWebRequest.Result.Success) -#else - if (_webRequest.isNetworkError || _webRequest.isHttpError) -#endif - { - YooLogger.Warning($"Failed to get asset bundle from web : {FileLoadPath} Error : {_webRequest.error}"); - _steps = ESteps.TryLoadWebFile; - _tryTimer = 0; - } - else - { - CacheBundle = DownloadHandlerAssetBundle.GetContent(_webRequest); - if (CacheBundle == null) - { - _steps = ESteps.Done; - Status = EStatus.Failed; - LastError = $"AssetBundle file is invalid : {MainBundleInfo.Bundle.BundleName}"; - YooLogger.Error(LastError); - } - else - { - _steps = ESteps.Done; - Status = EStatus.Succeed; - } - } - } - - // 7. 如果获取失败,重新尝试 - if (_steps == ESteps.TryLoadWebFile) - { - _tryTimer += Time.unscaledDeltaTime; - if (_tryTimer > 1f) - { - _webRequest.Dispose(); - _webRequest = null; - _steps = ESteps.LoadWebFile; - } + // 注意:释放下载句柄 + _downloader.DisposeHandler(); } } diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleLoaderBase.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleLoaderBase.cs index b559029..5c517e0 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleLoaderBase.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/BundleLoaderBase.cs @@ -144,6 +144,15 @@ namespace YooAsset } } + /// + /// 销毁资源包(安全模式) + /// + public void DestroySafely() + { + WaitForAsyncComplete(); + Destroy(); + } + /// /// 轮询更新 @@ -153,18 +162,15 @@ namespace YooAsset /// /// 销毁 /// - public virtual void Destroy(bool forceDestroy) + public virtual void Destroy() { IsDestroyed = true; // Check fatal - if (forceDestroy == false) - { - if (RefCount > 0) - throw new Exception($"Bundle file loader ref is not zero : {MainBundleInfo.Bundle.BundleName}"); - if (IsDone() == false) - throw new Exception($"Bundle file loader is not done : {MainBundleInfo.Bundle.BundleName}"); - } + if (RefCount > 0) + throw new Exception($"Bundle file loader ref is not zero : {MainBundleInfo.Bundle.BundleName}"); + if (IsDone() == false) + throw new Exception($"Bundle file loader is not done : {MainBundleInfo.Bundle.BundleName}"); if (CacheBundle != null) { diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleFileLoader.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleFileLoader.cs index 463e75d..a0f80b1 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleFileLoader.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleFileLoader.cs @@ -64,7 +64,8 @@ namespace YooAsset if (_steps == ESteps.Download) { int failedTryAgain = Impl.DownloadFailedTryAgain; - _downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); + _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain); + _downloader.SendRequest(); _steps = ESteps.CheckDownload; } @@ -93,7 +94,8 @@ namespace YooAsset { int failedTryAgain = Impl.DownloadFailedTryAgain; var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); - _unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); + _unpacker = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain); + _unpacker.SendRequest(); _steps = ESteps.CheckUnpack; } diff --git a/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleWebLoader.cs b/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleWebLoader.cs index 228c224..6879db7 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleWebLoader.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Loader/RawBundleWebLoader.cs @@ -47,11 +47,6 @@ namespace YooAsset _steps = ESteps.Website; FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; } - else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache) - { - _steps = ESteps.CheckFile; - FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; - } else { throw new System.NotImplementedException(MainBundleInfo.LoadMode.ToString()); @@ -62,7 +57,8 @@ namespace YooAsset if (_steps == ESteps.Download) { int failedTryAgain = Impl.DownloadFailedTryAgain; - _downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); + _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain); + _downloader.SendRequest(); _steps = ESteps.CheckDownload; } @@ -91,7 +87,8 @@ namespace YooAsset { int failedTryAgain = Impl.DownloadFailedTryAgain; var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); - _website = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); + _website = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain); + _website.SendRequest(); _steps = ESteps.CheckWebsite; } diff --git a/Assets/YooAsset/Runtime/AssetSystem/Provider/ProviderBase.cs b/Assets/YooAsset/Runtime/AssetSystem/Provider/ProviderBase.cs index 5afc00b..86ca7e8 100644 --- a/Assets/YooAsset/Runtime/AssetSystem/Provider/ProviderBase.cs +++ b/Assets/YooAsset/Runtime/AssetSystem/Provider/ProviderBase.cs @@ -123,7 +123,7 @@ namespace YooAsset /// /// 销毁资源对象 /// - public virtual void Destroy() + public void Destroy() { IsDestroyed = true; @@ -140,6 +140,22 @@ namespace YooAsset } } + /// + /// 销毁资源对象(安全模式) + /// + public void DestroySafely() + { + if (Status == EStatus.Loading || Status == EStatus.Checking) + { + WaitForAsyncComplete(); + Destroy(); + } + else + { + Destroy(); + } + } + /// /// 是否可以销毁 /// diff --git a/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs b/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs index b7a7723..81f77dd 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs @@ -98,34 +98,49 @@ namespace YooAsset /// - /// 开始下载资源文件 - /// 注意:只有第一次请求的参数才是有效的 + /// 创建下载器 + /// 注意:只有第一次请求的参数才有效 /// - public static DownloaderBase BeginDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60) + public static DownloaderBase CreateDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60) { // 查询存在的下载器 if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedDataFilePath, out var downloader)) - { return downloader; - } // 如果资源已经缓存 if (CacheSystem.IsCached(bundleInfo.Bundle.PackageName, bundleInfo.Bundle.CacheGUID)) { - var tempDownloader = new TempDownloader(bundleInfo); - return tempDownloader; + var completedDownloader = new CompletedDownloader(bundleInfo); + return completedDownloader; } // 创建新的下载器 + YooLogger.Log($"Beginning to download bundle : {bundleInfo.Bundle.BundleName} URL : {bundleInfo.RemoteMainURL}"); +#if UNITY_WEBGL + if (bundleInfo.Bundle.IsRawFile) { - YooLogger.Log($"Beginning to download file : {bundleInfo.Bundle.FileName} URL : {bundleInfo.RemoteMainURL}"); FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath); - bool breakDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize; - DownloaderBase newDownloader = new FileDownloader(bundleInfo, breakDownload); - newDownloader.SendRequest(failedTryAgain, timeout); + DownloaderBase newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout); _downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader); return newDownloader; } + else + { + WebDownloader newDownloader = new WebDownloader(bundleInfo, failedTryAgain, timeout); + _downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader); + return newDownloader; + } +#else + FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath); + bool resumeDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize; + DownloaderBase newDownloader; + if (resumeDownload) + newDownloader = new FileResumeDownloader(bundleInfo, failedTryAgain, timeout); + else + newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout); + _downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader); + return newDownloader; +#endif } /// @@ -139,14 +154,14 @@ namespace YooAsset else webRequest = new UnityWebRequest(requestURL, UnityWebRequest.kHttpVerbGET); - SetUnityWebRequest(webRequest); + SetUnityWebRequestParam(webRequest); return webRequest; } /// /// 设置网络请求的自定义参数 /// - public static void SetUnityWebRequest(UnityWebRequest webRequest) + private static void SetUnityWebRequestParam(UnityWebRequest webRequest) { if (RedirectLimit >= 0) webRequest.redirectLimit = RedirectLimit; diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs new file mode 100644 index 0000000..8c798f8 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs @@ -0,0 +1,23 @@ + +namespace YooAsset +{ + internal sealed class CompletedDownloader : DownloaderBase + { + public CompletedDownloader(BundleInfo bundleInfo) : base(bundleInfo, 0, 0) + { + _downloadProgress = 1f; + _downloadedBytes = (ulong)bundleInfo.Bundle.FileSize; + _status = EStatus.Succeed; + } + + public override void SendRequest(params object[] param) + { + } + public override void Update() + { + } + public override void Abort() + { + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/Downloader/CompletedDownloader.cs.meta diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs index 7866417..f187903 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs @@ -1,39 +1,41 @@ - +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + namespace YooAsset { internal abstract class DownloaderBase { - protected enum ESteps + public enum EStatus { - None, - CheckTempFile, - WaitingCheckTempFile, - PrepareDownload, - CreateResumeDownloader, - CreateGeneralDownloader, - CheckDownload, - VerifyTempFile, - WaitingVerifyTempFile, - CachingFile, - TryAgain, + None = 0, Succeed, - Failed, + Failed } protected readonly BundleInfo _bundleInfo; - - protected ESteps _steps = ESteps.None; - - protected int _timeout; + protected readonly int _timeout; protected int _failedTryAgain; - protected int _requestCount; - protected string _requestURL; + protected UnityWebRequest _webRequest; + protected EStatus _status = EStatus.None; protected string _lastError = string.Empty; protected long _lastCode = 0; + + // 请求次数 + protected int _requestCount = 0; + protected string _requestURL; + + // 下载进度 protected float _downloadProgress = 0f; protected ulong _downloadedBytes = 0; + // 超时相关 + protected bool _isAbort = false; + protected ulong _latestDownloadBytes; + protected float _latestDownloadRealtime; + protected float _tryAgainTimer; + /// /// 是否等待异步结束 /// 警告:只能用于解压APP内部资源 @@ -57,41 +59,31 @@ namespace YooAsset } - public DownloaderBase(BundleInfo bundleInfo) + public DownloaderBase(BundleInfo bundleInfo, int failedTryAgain, int timeout) { _bundleInfo = bundleInfo; + _failedTryAgain = failedTryAgain; + _timeout = timeout; } - public void SendRequest(int failedTryAgain, int timeout) - { - if (_steps == ESteps.None) - { - _failedTryAgain = failedTryAgain; - _timeout = timeout; - _steps = ESteps.CheckTempFile; - } - } + public abstract void SendRequest(params object[] param); public abstract void Update(); public abstract void Abort(); /// - /// 获取网络请求地址 + /// 获取下载文件的大小 /// - protected string GetRequestURL() + /// + public long GetDownloadFileSize() { - // 轮流返回请求地址 - _requestCount++; - if (_requestCount % 2 == 0) - return _bundleInfo.RemoteFallbackURL; - else - return _bundleInfo.RemoteMainURL; + return _bundleInfo.Bundle.FileSize; } /// - /// 获取资源包信息 + /// 获取下载文件的资源包名 /// - public BundleInfo GetBundleInfo() + public string GetDownloadBundleName() { - return _bundleInfo; + return _bundleInfo.Bundle.BundleName; } /// @@ -99,7 +91,7 @@ namespace YooAsset /// public bool IsDone() { - return _steps == ESteps.Succeed || _steps == ESteps.Failed; + return _status == EStatus.Succeed || _status == EStatus.Failed; } /// @@ -107,7 +99,7 @@ namespace YooAsset /// public bool HasError() { - return _steps == ESteps.Failed; + return _status == EStatus.Failed; } /// @@ -133,5 +125,69 @@ namespace YooAsset { return $"Failed to download : {_requestURL} Error : {_lastError} Code : {_lastCode}"; } + + + /// + /// 获取网络请求地址 + /// + protected string GetRequestURL() + { + // 轮流返回请求地址 + _requestCount++; + if (_requestCount % 2 == 0) + return _bundleInfo.RemoteFallbackURL; + else + return _bundleInfo.RemoteMainURL; + } + + /// + /// 超时判定方法 + /// + protected void CheckTimeout() + { + // 注意:在连续时间段内无新增下载数据及判定为超时 + if (_isAbort == false) + { + if (_latestDownloadBytes != DownloadedBytes) + { + _latestDownloadBytes = DownloadedBytes; + _latestDownloadRealtime = Time.realtimeSinceStartup; + } + + float offset = Time.realtimeSinceStartup - _latestDownloadRealtime; + if (offset > _timeout) + { + YooLogger.Warning($"Web file request timeout : {_requestURL}"); + _webRequest.Abort(); + _isAbort = true; + } + } + } + + /// + /// 缓存下载文件 + /// + protected void CachingFile(string tempFilePath) + { + string infoFilePath = _bundleInfo.Bundle.CachedInfoFilePath; + string dataFilePath = _bundleInfo.Bundle.CachedDataFilePath; + string dataFileCRC = _bundleInfo.Bundle.FileCRC; + long dataFileSize = _bundleInfo.Bundle.FileSize; + + if (File.Exists(infoFilePath)) + File.Delete(infoFilePath); + if (File.Exists(dataFilePath)) + File.Delete(dataFilePath); + + FileInfo fileInfo = new FileInfo(tempFilePath); + fileInfo.MoveTo(dataFilePath); + + // 写入信息文件记录验证数据 + CacheFileInfo.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize); + + // 记录缓存文件 + var wrapper = new PackageCache.RecordWrapper(infoFilePath, dataFilePath, dataFileCRC, dataFileSize); + CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper); + } } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs new file mode 100644 index 0000000..c9fca2b --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace YooAsset +{ + /// + /// 普通的下载器 + /// + internal sealed class FileGeneralDownloader : DownloaderBase + { + private enum ESteps + { + None, + PrepareDownload, + CreateDownloader, + CheckDownload, + VerifyTempFile, + WaitingVerifyTempFile, + CachingFile, + TryAgain, + Done, + } + + private readonly string _tempFilePath; + private VerifyTempFileOperation _verifyFileOp = null; + private ESteps _steps = ESteps.None; + + + public FileGeneralDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout) + { + _tempFilePath = bundleInfo.Bundle.TempDataFilePath; + } + public override void SendRequest(params object[] param) + { + if (_steps == ESteps.None) + { + _steps = ESteps.PrepareDownload; + } + } + public override void Update() + { + if (_steps == ESteps.None) + return; + if (IsDone()) + return; + + // 准备下载 + if (_steps == ESteps.PrepareDownload) + { + // 重置变量 + _downloadProgress = 0f; + _downloadedBytes = 0; + + // 重置变量 + _isAbort = false; + _latestDownloadBytes = 0; + _latestDownloadRealtime = Time.realtimeSinceStartup; + _tryAgainTimer = 0f; + + // 删除临时文件 + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); + + // 获取请求地址 + _requestURL = GetRequestURL(); + _steps = ESteps.CreateDownloader; + } + + // 创建下载器 + if (_steps == ESteps.CreateDownloader) + { + _webRequest = DownloadSystem.NewRequest(_requestURL); + DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath); + handler.removeFileOnAbort = true; + _webRequest.downloadHandler = handler; + _webRequest.disposeDownloadHandlerOnDispose = true; + _webRequest.SendWebRequest(); + _steps = ESteps.CheckDownload; + } + + // 检测下载结果 + if (_steps == ESteps.CheckDownload) + { + _downloadProgress = _webRequest.downloadProgress; + _downloadedBytes = _webRequest.downloadedBytes; + if (_webRequest.isDone == false) + { + CheckTimeout(); + return; + } + + bool hasError = false; + + // 检查网络错误 +#if UNITY_2020_3_OR_NEWER + if (_webRequest.result != UnityWebRequest.Result.Success) + { + hasError = true; + _lastError = _webRequest.error; + _lastCode = _webRequest.responseCode; + } +#else + if (_webRequest.isNetworkError || _webRequest.isHttpError) + { + hasError = true; + _lastError = _webRequest.error; + _lastCode = _webRequest.responseCode; + } +#endif + + // 如果网络异常 + if (hasError) + { + // 下载失败之后删除文件 + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); + + _steps = ESteps.TryAgain; + } + else + { + _steps = ESteps.VerifyTempFile; + } + + // 最终释放下载器 + DisposeWebRequest(); + } + + // 验证下载文件 + if (_steps == ESteps.VerifyTempFile) + { + VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); + _verifyFileOp = VerifyTempFileOperation.CreateOperation(element); + OperationSystem.StartOperation(_verifyFileOp); + _steps = ESteps.WaitingVerifyTempFile; + } + + // 等待验证完成 + if (_steps == ESteps.WaitingVerifyTempFile) + { + if (WaitForAsyncComplete) + _verifyFileOp.Update(); + + if (_verifyFileOp.IsDone == false) + return; + + if (_verifyFileOp.Status == EOperationStatus.Succeed) + { + _steps = ESteps.CachingFile; + } + else + { + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); + + _lastError = _verifyFileOp.Error; + _steps = ESteps.TryAgain; + } + } + + // 缓存下载文件 + if (_steps == ESteps.CachingFile) + { + try + { + CachingFile(_tempFilePath); + _status = EStatus.Succeed; + _steps = ESteps.Done; + _lastError = string.Empty; + _lastCode = 0; + } + catch (Exception e) + { + _lastError = e.Message; + _steps = ESteps.TryAgain; + } + } + + // 重新尝试下载 + if (_steps == ESteps.TryAgain) + { + if (_failedTryAgain <= 0) + { + ReportError(); + _status = EStatus.Failed; + _steps = ESteps.Done; + return; + } + + _tryAgainTimer += Time.unscaledDeltaTime; + if (_tryAgainTimer > 1f) + { + _failedTryAgain--; + _steps = ESteps.PrepareDownload; + ReportWarning(); + YooLogger.Warning($"Try again download : {_requestURL}"); + } + } + } + public override void Abort() + { + if (IsDone() == false) + { + _status = EStatus.Failed; + _steps = ESteps.Done; + _lastError = "user abort"; + _lastCode = 0; + DisposeWebRequest(); + } + } + + private void DisposeWebRequest() + { + if (_webRequest != null) + { + _webRequest.Dispose(); + _webRequest = null; + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileGeneralDownloader.cs.meta diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs similarity index 61% rename from Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs rename to Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs index 943c7b1..070f7c4 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs @@ -7,28 +7,47 @@ using UnityEngine.Networking; namespace YooAsset { - internal sealed class FileDownloader : DownloaderBase + /// + /// 断点续传下载器 + /// + internal sealed class FileResumeDownloader : DownloaderBase { - private readonly bool _breakResume; + private enum ESteps + { + None, + CheckTempFile, + WaitingCheckTempFile, + PrepareDownload, + CreateDownloader, + CheckDownload, + VerifyTempFile, + WaitingVerifyTempFile, + CachingFile, + TryAgain, + Done, + } + private readonly string _tempFilePath; - private UnityWebRequest _webRequest = null; private DownloadHandlerFileRange _downloadHandle = null; private VerifyTempFileOperation _checkFileOp = null; private VerifyTempFileOperation _verifyFileOp = null; + private ESteps _steps = ESteps.None; // 重置变量 - private bool _isAbort = false; private ulong _fileOriginLength; - private ulong _latestDownloadBytes; - private float _latestDownloadRealtime; - private float _tryAgainTimer; - public FileDownloader(BundleInfo bundleInfo, bool breakResume) : base(bundleInfo) + public FileResumeDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout) { - _breakResume = breakResume; _tempFilePath = bundleInfo.Bundle.TempDataFilePath; } + public override void SendRequest(params object[] param) + { + if (_steps == ESteps.None) + { + _steps = ESteps.CheckTempFile; + } + } public override void Update() { if (_steps == ESteps.None) @@ -39,7 +58,7 @@ namespace YooAsset // 检测临时文件 if (_steps == ESteps.CheckTempFile) { - VerifyTempElement element = new VerifyTempElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); + VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); _checkFileOp = VerifyTempFileOperation.CreateOperation(element); OperationSystem.StartOperation(_checkFileOp); _steps = ESteps.WaitingCheckTempFile; @@ -75,38 +94,21 @@ namespace YooAsset // 重置变量 _downloadProgress = 0f; _downloadedBytes = 0; + + // 重置变量 _isAbort = false; - _fileOriginLength = 0; _latestDownloadBytes = 0; _latestDownloadRealtime = Time.realtimeSinceStartup; _tryAgainTimer = 0f; + _fileOriginLength = 0; // 获取请求地址 _requestURL = GetRequestURL(); - - if (_breakResume) - _steps = ESteps.CreateResumeDownloader; - else - _steps = ESteps.CreateGeneralDownloader; + _steps = ESteps.CreateDownloader; } - // 创建普通的下载器 - if (_steps == ESteps.CreateGeneralDownloader) - { - if (File.Exists(_tempFilePath)) - File.Delete(_tempFilePath); - - _webRequest = DownloadSystem.NewRequest(_requestURL); - DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath); - handler.removeFileOnAbort = true; - _webRequest.downloadHandler = handler; - _webRequest.disposeDownloadHandlerOnDispose = true; - _webRequest.SendWebRequest(); - _steps = ESteps.CheckDownload; - } - - // 创建断点续传下载器 - if (_steps == ESteps.CreateResumeDownloader) + // 创建下载器 + if (_steps == ESteps.CreateDownloader) { long fileLength = -1; if (File.Exists(_tempFilePath)) @@ -167,24 +169,15 @@ namespace YooAsset // 如果网络异常 if (hasError) { - if (_breakResume) + // 注意:下载断点续传文件发生特殊错误码之后删除文件 + if (DownloadSystem.ClearFileResponseCodes != null) { - // 注意:下载断点续传文件发生特殊错误码之后删除文件 - if (DownloadSystem.ClearFileResponseCodes != null) + if (DownloadSystem.ClearFileResponseCodes.Contains(_webRequest.responseCode)) { - if (DownloadSystem.ClearFileResponseCodes.Contains(_webRequest.responseCode)) - { - if (File.Exists(_tempFilePath)) - File.Delete(_tempFilePath); - } + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); } } - else - { - // 注意:非断点续传下载失败之后删除文件 - if (File.Exists(_tempFilePath)) - File.Delete(_tempFilePath); - } _steps = ESteps.TryAgain; } @@ -193,14 +186,14 @@ namespace YooAsset _steps = ESteps.VerifyTempFile; } - // 释放下载器 + // 最终释放下载器 DisposeWebRequest(); } // 验证下载文件 if (_steps == ESteps.VerifyTempFile) { - VerifyTempElement element = new VerifyTempElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); + VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); _verifyFileOp = VerifyTempFileOperation.CreateOperation(element); OperationSystem.StartOperation(_verifyFileOp); _steps = ESteps.WaitingVerifyTempFile; @@ -234,29 +227,11 @@ namespace YooAsset { try { - string infoFilePath = _bundleInfo.Bundle.CachedInfoFilePath; - string dataFilePath = _bundleInfo.Bundle.CachedDataFilePath; - string dataFileCRC = _bundleInfo.Bundle.FileCRC; - long dataFileSize = _bundleInfo.Bundle.FileSize; - - if (File.Exists(infoFilePath)) - File.Delete(infoFilePath); - if (File.Exists(dataFilePath)) - File.Delete(dataFilePath); - - FileInfo fileInfo = new FileInfo(_tempFilePath); - fileInfo.MoveTo(dataFilePath); - - // 写入信息文件记录验证数据 - CacheFileInfo.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize); - - // 记录缓存文件 - var wrapper = new PackageCache.RecordWrapper(infoFilePath, dataFilePath, dataFileCRC, dataFileSize); - CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper); - + CachingFile(_tempFilePath); + _status = EStatus.Succeed; + _steps = ESteps.Done; _lastError = string.Empty; _lastCode = 0; - _steps = ESteps.Succeed; } catch (Exception e) { @@ -271,7 +246,8 @@ namespace YooAsset if (_failedTryAgain <= 0) { ReportError(); - _steps = ESteps.Failed; + _status = EStatus.Failed; + _steps = ESteps.Done; return; } @@ -289,33 +265,14 @@ namespace YooAsset { if (IsDone() == false) { - _steps = ESteps.Failed; + _status = EStatus.Failed; + _steps = ESteps.Done; _lastError = "user abort"; _lastCode = 0; DisposeWebRequest(); } } - private void CheckTimeout() - { - // 注意:在连续时间段内无新增下载数据及判定为超时 - if (_isAbort == false) - { - if (_latestDownloadBytes != DownloadedBytes) - { - _latestDownloadBytes = DownloadedBytes; - _latestDownloadRealtime = Time.realtimeSinceStartup; - } - - float offset = Time.realtimeSinceStartup - _latestDownloadRealtime; - if (offset > _timeout) - { - YooLogger.Warning($"Web file request timeout : {_requestURL}"); - _webRequest.Abort(); - _isAbort = true; - } - } - } private void DisposeWebRequest() { if (_downloadHandle != null) diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs.meta new file mode 100644 index 0000000..0d2cdfd --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileResumeDownloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a439c9a8f36dcc942b92a8e8af927237 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs deleted file mode 100644 index 389a570..0000000 --- a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/TempDownloader.cs +++ /dev/null @@ -1,20 +0,0 @@ - -namespace YooAsset -{ - internal sealed class TempDownloader : DownloaderBase - { - public TempDownloader(BundleInfo bundleInfo) : base(bundleInfo) - { - _downloadProgress = 1f; - _downloadedBytes = (ulong)bundleInfo.Bundle.FileSize; - _steps = ESteps.Succeed; - } - - public override void Update() - { - } - public override void Abort() - { - } - } -} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs new file mode 100644 index 0000000..d4a1f70 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs @@ -0,0 +1,209 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEngine.Networking; + +namespace YooAsset +{ + internal sealed class WebDownloader : DownloaderBase + { + private enum ESteps + { + None, + PrepareDownload, + CreateDownloader, + CheckDownload, + VerifyTempFile, + WaitingVerifyTempFile, + CachingFile, + TryAgain, + Done, + } + + private bool _keepDownloadHandleLife = false; + private DownloadHandlerAssetBundle _downloadhandler; + private ESteps _steps = ESteps.None; + + + public WebDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout) + { + } + public override void SendRequest(params object[] param) + { + if (_steps == ESteps.None) + { + _keepDownloadHandleLife = (bool)param[0]; + _steps = ESteps.PrepareDownload; + } + } + public override void Update() + { + if (_steps == ESteps.None) + return; + if (IsDone()) + return; + + // 创建下载器 + if (_steps == ESteps.PrepareDownload) + { + // 重置变量 + _downloadProgress = 0f; + _downloadedBytes = 0; + + // 重置变量 + _isAbort = false; + _latestDownloadBytes = 0; + _latestDownloadRealtime = Time.realtimeSinceStartup; + _tryAgainTimer = 0f; + + // 获取请求地址 + _requestURL = GetRequestURL(); + _steps = ESteps.CreateDownloader; + } + + // 创建下载器 + if (_steps == ESteps.CreateDownloader) + { + _webRequest = DownloadSystem.NewRequest(_requestURL); + + if (CacheSystem.DisableUnityCacheOnWebGL) + { + uint crc = _bundleInfo.Bundle.UnityCRC; + _downloadhandler = new DownloadHandlerAssetBundle(_requestURL, crc); + _downloadhandler.autoLoadAssetBundle = false; + } + else + { + uint crc = _bundleInfo.Bundle.UnityCRC; + var hash = Hash128.Parse(_bundleInfo.Bundle.FileHash); + _downloadhandler = new DownloadHandlerAssetBundle(_requestURL, hash, crc); + _downloadhandler.autoLoadAssetBundle = false; + } + + _webRequest.downloadHandler = _downloadhandler; + _webRequest.disposeDownloadHandlerOnDispose = false; + _webRequest.SendWebRequest(); + _steps = ESteps.CheckDownload; + } + + // 检测下载结果 + if (_steps == ESteps.CheckDownload) + { + _downloadProgress = _webRequest.downloadProgress; + _downloadedBytes = _webRequest.downloadedBytes; + if (_webRequest.isDone == false) + { + CheckTimeout(); + return; + } + + bool hasError = false; + + // 检查网络错误 +#if UNITY_2020_3_OR_NEWER + if (_webRequest.result != UnityWebRequest.Result.Success) + { + hasError = true; + _lastError = _webRequest.error; + _lastCode = _webRequest.responseCode; + } +#else + if (_webRequest.isNetworkError || _webRequest.isHttpError) + { + hasError = true; + _lastError = _webRequest.error; + _lastCode = _webRequest.responseCode; + } +#endif + + // 如果网络异常 + if (hasError) + { + _steps = ESteps.TryAgain; + } + else + { + _status = EStatus.Succeed; + _steps = ESteps.Done; + _lastError = string.Empty; + _lastCode = 0; + } + + // 最终释放请求 + DisposeRequest(); + + if (_keepDownloadHandleLife == false) + DisposeHandler(); + } + + // 重新尝试下载 + if (_steps == ESteps.TryAgain) + { + if (_failedTryAgain <= 0) + { + DisposeRequest(); + DisposeHandler(); + ReportError(); + _status = EStatus.Failed; + _steps = ESteps.Done; + return; + } + + _tryAgainTimer += Time.unscaledDeltaTime; + if (_tryAgainTimer > 1f) + { + _failedTryAgain--; + _steps = ESteps.PrepareDownload; + ReportWarning(); + YooLogger.Warning($"Try again download : {_requestURL}"); + } + } + } + public override void Abort() + { + if (IsDone() == false) + { + _status = EStatus.Failed; + _steps = ESteps.Done; + _lastError = "user abort"; + _lastCode = 0; + + DisposeRequest(); + DisposeHandler(); + } + } + private void DisposeRequest() + { + if (_webRequest != null) + { + _webRequest.Dispose(); + _webRequest = null; + } + } + + /// + /// 获取资源包 + /// + public AssetBundle GetAssetBundle() + { + if (_downloadhandler != null) + return _downloadhandler.assetBundle; + + return null; + } + + /// + /// 释放下载句柄 + /// + public void DisposeHandler() + { + if (_downloadhandler != null) + { + _downloadhandler.Dispose(); + _downloadhandler = null; + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs.meta new file mode 100644 index 0000000..690ea8f --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/WebDownloader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 41bc4bc56f59ddb4b8925f9536bbbfbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs index 6c0a213..9839a2c 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs @@ -2,38 +2,26 @@ using System.Collections; using System.Collections.Generic; using UnityEngine.Networking; +using UnityEngine; namespace YooAsset { - /// - /// 下载器 - /// 说明:UnityWebRequest(UWR) supports reading streaming assets since 2017.1 - /// - internal class UnityWebDataRequester + internal class UnityWebDataRequester : UnityWebRequesterBase { - private UnityWebRequest _webRequest; - private UnityWebRequestAsyncOperation _operationHandle; - - /// - /// 请求URL地址 - /// - public string URL { private set; get; } - - /// /// 发送GET请求 /// - /// 超时:从请求开始计时 - public void SendRequest(string url, int timeout = 0) + public void SendRequest(string url, int timeout = 60) { if (_webRequest == null) { URL = url; + ResetTimeout(timeout); + _webRequest = DownloadSystem.NewRequest(URL); DownloadHandlerBuffer handler = new DownloadHandlerBuffer(); _webRequest.downloadHandler = handler; _webRequest.disposeDownloadHandlerOnDispose = true; - _webRequest.timeout = timeout; _operationHandle = _webRequest.SendWebRequest(); } } @@ -59,65 +47,5 @@ namespace YooAsset else return null; } - - /// - /// 释放下载器 - /// - public void Dispose() - { - if (_webRequest != null) - { - _webRequest.Dispose(); - _webRequest = null; - _operationHandle = null; - } - } - - /// - /// 是否完毕(无论成功失败) - /// - public bool IsDone() - { - if (_operationHandle == null) - return false; - return _operationHandle.isDone; - } - - /// - /// 下载进度 - /// - public float Progress() - { - if (_operationHandle == null) - return 0; - return _operationHandle.progress; - } - - /// - /// 下载是否发生错误 - /// - public bool HasError() - { -#if UNITY_2020_3_OR_NEWER - return _webRequest.result != UnityWebRequest.Result.Success; -#else - if (_webRequest.isNetworkError || _webRequest.isHttpError) - return true; - else - return false; -#endif - } - - /// - /// 获取错误信息 - /// - public string GetError() - { - if (_webRequest != null) - { - return $"URL : {URL} Error : {_webRequest.error}"; - } - return string.Empty; - } } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs index f5c08bc..4358fcb 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs @@ -6,129 +6,25 @@ using UnityEngine; namespace YooAsset { - /// - /// 下载器 - /// 说明:UnityWebRequest(UWR) supports reading streaming assets since 2017.1 - /// - internal class UnityWebFileRequester + internal class UnityWebFileRequester : UnityWebRequesterBase { - private UnityWebRequest _webRequest; - private UnityWebRequestAsyncOperation _operationHandle; - - // 超时相关 - private float _timeout; - private bool _isAbort = false; - private ulong _latestDownloadBytes; - private float _latestDownloadRealtime; - - /// - /// 请求URL地址 - /// - public string URL { private set; get; } - - /// /// 发送GET请求 /// - public void SendRequest(string url, string savePath, float timeout = 60) + public void SendRequest(string url, string fileSavePath, int timeout = 60) { if (_webRequest == null) { URL = url; - _timeout = timeout; - _latestDownloadBytes = 0; - _latestDownloadRealtime = Time.realtimeSinceStartup; + ResetTimeout(timeout); _webRequest = DownloadSystem.NewRequest(URL); - DownloadHandlerFile handler = new DownloadHandlerFile(savePath); + DownloadHandlerFile handler = new DownloadHandlerFile(fileSavePath); handler.removeFileOnAbort = true; _webRequest.downloadHandler = handler; _webRequest.disposeDownloadHandlerOnDispose = true; _operationHandle = _webRequest.SendWebRequest(); } - } - - /// - /// 释放下载器 - /// - public void Dispose() - { - if (_webRequest != null) - { - _webRequest.Dispose(); - _webRequest = null; - _operationHandle = null; - } - } - - /// - /// 是否完毕(无论成功失败) - /// - public bool IsDone() - { - if (_operationHandle == null) - return false; - return _operationHandle.isDone; - } - - /// - /// 下载进度 - /// - public float Progress() - { - if (_operationHandle == null) - return 0; - return _operationHandle.progress; - } - - /// - /// 下载是否发生错误 - /// - public bool HasError() - { -#if UNITY_2020_3_OR_NEWER - return _webRequest.result != UnityWebRequest.Result.Success; -#else - if (_webRequest.isNetworkError || _webRequest.isHttpError) - return true; - else - return false; -#endif - } - - /// - /// 获取错误信息 - /// - public string GetError() - { - if (_webRequest != null) - { - return $"URL : {URL} Error : {_webRequest.error}"; - } - return string.Empty; - } - - /// - /// 检测超时 - /// - public void CheckTimeout() - { - // 注意:在连续时间段内无新增下载数据及判定为超时 - if (_isAbort == false) - { - if (_latestDownloadBytes != _webRequest.downloadedBytes) - { - _latestDownloadBytes = _webRequest.downloadedBytes; - _latestDownloadRealtime = Time.realtimeSinceStartup; - } - - float offset = Time.realtimeSinceStartup - _latestDownloadRealtime; - if (offset > _timeout) - { - _webRequest.Abort(); - _isAbort = true; - } - } - } + } } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs new file mode 100644 index 0000000..86753f7 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using UnityEngine.Networking; +using UnityEngine; + +namespace YooAsset +{ + internal abstract class UnityWebRequesterBase + { + protected UnityWebRequest _webRequest; + protected UnityWebRequestAsyncOperation _operationHandle; + + // 超时相关 + private float _timeout; + private bool _isAbort = false; + private ulong _latestDownloadBytes; + private float _latestDownloadRealtime; + + /// + /// 请求URL地址 + /// + public string URL { protected set; get; } + + + protected void ResetTimeout(float timeout) + { + _timeout = timeout; + _latestDownloadBytes = 0; + _latestDownloadRealtime = Time.realtimeSinceStartup; + } + + /// + /// 释放下载器 + /// + public void Dispose() + { + if (_webRequest != null) + { + _webRequest.Dispose(); + _webRequest = null; + _operationHandle = null; + } + } + + /// + /// 是否完毕(无论成功失败) + /// + public bool IsDone() + { + if (_operationHandle == null) + return false; + return _operationHandle.isDone; + } + + /// + /// 下载进度 + /// + public float Progress() + { + if (_operationHandle == null) + return 0; + return _operationHandle.progress; + } + + /// + /// 下载是否发生错误 + /// + public bool HasError() + { +#if UNITY_2020_3_OR_NEWER + return _webRequest.result != UnityWebRequest.Result.Success; +#else + if (_webRequest.isNetworkError || _webRequest.isHttpError) + return true; + else + return false; +#endif + } + + /// + /// 获取错误信息 + /// + public string GetError() + { + if (_webRequest != null) + { + return $"URL : {URL} Error : {_webRequest.error}"; + } + return string.Empty; + } + + /// + /// 检测超时 + /// + public void CheckTimeout() + { + // 注意:在连续时间段内无新增下载数据及判定为超时 + if (_isAbort == false) + { + if (_latestDownloadBytes != _webRequest.downloadedBytes) + { + _latestDownloadBytes = _webRequest.downloadedBytes; + _latestDownloadRealtime = Time.realtimeSinceStartup; + } + + float offset = Time.realtimeSinceStartup - _latestDownloadRealtime; + if (offset > _timeout) + { + _webRequest.Abort(); + _isAbort = true; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs.meta new file mode 100644 index 0000000..c7401fe --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebRequesterBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4e5c3c1a1655a8b41b585c6811201583 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/InitializeParameters.cs b/Assets/YooAsset/Runtime/InitializeParameters.cs index 1abd0bd..9436685 100644 --- a/Assets/YooAsset/Runtime/InitializeParameters.cs +++ b/Assets/YooAsset/Runtime/InitializeParameters.cs @@ -20,6 +20,11 @@ namespace YooAsset /// 联机运行模式 /// HostPlayMode, + + /// + /// WebGL运行模式 + /// + WebPlayMode, } /// @@ -90,4 +95,20 @@ namespace YooAsset /// public IRemoteServices RemoteServices = null; } + + /// + /// WebGL运行模式的初始化参数 + /// + public class WebPlayModeParameters : InitializeParameters + { + /// + /// 内置资源查询服务接口 + /// + public IQueryServices QueryServices = null; + + /// + /// 远端资源地址查询服务类 + /// + public IRemoteServices RemoteServices = null; + } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/ManifestTools.cs b/Assets/YooAsset/Runtime/PackageSystem/ManifestTools.cs index 30e1674..e096143 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/ManifestTools.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/ManifestTools.cs @@ -61,6 +61,7 @@ namespace YooAsset { var packageBundle = manifest.BundleList[i]; buffer.WriteUTF8(packageBundle.BundleName); + buffer.WriteUInt32(packageBundle.UnityCRC); buffer.WriteUTF8(packageBundle.FileHash); buffer.WriteUTF8(packageBundle.FileCRC); buffer.WriteInt64(packageBundle.FileSize); @@ -139,6 +140,7 @@ namespace YooAsset { var packageBundle = new PackageBundle(); packageBundle.BundleName = buffer.ReadUTF8(); + packageBundle.UnityCRC = buffer.ReadUInt32(); packageBundle.FileHash = buffer.ReadUTF8(); packageBundle.FileCRC = buffer.ReadUTF8(); packageBundle.FileSize = buffer.ReadInt64(); @@ -208,7 +210,7 @@ namespace YooAsset } /// - /// 批量转换解压为BundleInfo + /// 批量转换为解压BundleInfo /// public static List ConvertToUnpackInfos(List unpackList) { diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/DownloaderOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/DownloaderOperation.cs index 352c617..6d727e4 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/DownloaderOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/DownloaderOperation.cs @@ -135,8 +135,6 @@ namespace YooAsset if (downloader.IsDone() == false) continue; - BundleInfo bundleInfo = downloader.GetBundleInfo(); - // 检测是否下载失败 if (downloader.HasError()) { @@ -148,7 +146,7 @@ namespace YooAsset // 下载成功 _removeList.Add(downloader); _cachedDownloadCount++; - _cachedDownloadBytes += bundleInfo.Bundle.FileSize; + _cachedDownloadBytes += downloader.GetDownloadFileSize(); } // 移除已经完成的下载器(无论成功或失败) @@ -177,8 +175,9 @@ namespace YooAsset { int index = _downloadList.Count - 1; var bundleInfo = _downloadList[index]; - var operation = DownloadSystem.BeginDownload(bundleInfo, _failedTryAgain, _timeout); - _downloaders.Add(operation); + var downloader = DownloadSystem.CreateDownload(bundleInfo, _failedTryAgain, _timeout); + downloader.SendRequest(); + _downloaders.Add(downloader); _downloadList.RemoveAt(index); OnStartDownloadFileCallback?.Invoke(bundleInfo.Bundle.BundleName, bundleInfo.Bundle.FileSize); } @@ -190,7 +189,7 @@ namespace YooAsset if (_failedList.Count > 0) { var failedDownloader = _failedList[0]; - string fileName = failedDownloader.GetBundleInfo().Bundle.BundleName; + string fileName = failedDownloader.GetDownloadBundleName(); _steps = ESteps.Done; Status = EOperationStatus.Failed; Error = $"Failed to download file : {fileName}"; diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/InitializationOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/InitializationOperation.cs index 66496ad..092b000 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/InitializationOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/InitializationOperation.cs @@ -364,6 +364,93 @@ namespace YooAsset } } + /// + /// WebGL运行模式的初始化操作 + /// + internal sealed class WebPlayModeInitializationOperation : InitializationOperation + { + private enum ESteps + { + None, + QueryWebPackageVersion, + LoadWebManifest, + Done, + } + + private readonly WebPlayModeImpl _impl; + private readonly string _packageName; + private QueryBuildinPackageVersionOperation _queryWebPackageVersionOp; + private LoadBuildinManifestOperation _loadWebManifestOp; + private ESteps _steps = ESteps.None; + + internal WebPlayModeInitializationOperation(WebPlayModeImpl impl, string packageName) + { + _impl = impl; + _packageName = packageName; + } + internal override void Start() + { + _steps = ESteps.QueryWebPackageVersion; + } + internal override void Update() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.QueryWebPackageVersion) + { + if (_queryWebPackageVersionOp == null) + { + _queryWebPackageVersionOp = new QueryBuildinPackageVersionOperation(_packageName); + OperationSystem.StartOperation(_queryWebPackageVersionOp); + } + + if (_queryWebPackageVersionOp.IsDone == false) + return; + + if (_queryWebPackageVersionOp.Status == EOperationStatus.Succeed) + { + _steps = ESteps.LoadWebManifest; + } + else + { + // 注意:WebGL平台可能因为网络的原因会导致请求失败。如果内置清单不存在或者超时也不需要报错! + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + string error = _queryWebPackageVersionOp.Error; + YooLogger.Log($"Failed to load web package version file : {error}"); + } + } + + if (_steps == ESteps.LoadWebManifest) + { + if (_loadWebManifestOp == null) + { + _loadWebManifestOp = new LoadBuildinManifestOperation(_packageName, _queryWebPackageVersionOp.PackageVersion); + OperationSystem.StartOperation(_loadWebManifestOp); + } + + Progress = _loadWebManifestOp.Progress; + if (_loadWebManifestOp.IsDone == false) + return; + + if (_loadWebManifestOp.Status == EOperationStatus.Succeed) + { + PackageVersion = _loadWebManifestOp.Manifest.PackageVersion; + _impl.ActiveManifest = _loadWebManifestOp.Manifest; + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _loadWebManifestOp.Error; + } + } + } + } + /// /// 应用程序水印 /// diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/DeserializeManifestOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/DeserializeManifestOperation.cs index b1e4907..51deab9 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/DeserializeManifestOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/DeserializeManifestOperation.cs @@ -196,6 +196,7 @@ namespace YooAsset { var packageBundle = new PackageBundle(); packageBundle.BundleName = _buffer.ReadUTF8(); + packageBundle.UnityCRC = _buffer.ReadUInt32(); packageBundle.FileHash = _buffer.ReadUTF8(); packageBundle.FileCRC = _buffer.ReadUTF8(); packageBundle.FileSize = _buffer.ReadInt64(); diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs new file mode 100644 index 0000000..3344b95 --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs @@ -0,0 +1,150 @@ + +namespace YooAsset +{ + internal class LoadRemoteManifestOperation : AsyncOperationBase + { + private enum ESteps + { + None, + DownloadPackageHashFile, + DownloadManifestFile, + VerifyFileHash, + CheckDeserializeManifest, + Done, + } + + private static int RequestCount = 0; + private readonly IRemoteServices _remoteServices; + private readonly string _packageName; + private readonly string _packageVersion; + private readonly int _timeout; + private QueryRemotePackageHashOperation _queryRemotePackageHashOp; + private UnityWebDataRequester _downloader; + private DeserializeManifestOperation _deserializer; + private byte[] _fileData; + private ESteps _steps = ESteps.None; + + /// + /// 加载的清单实例 + /// + public PackageManifest Manifest { private set; get; } + + + internal LoadRemoteManifestOperation(IRemoteServices remoteServices, string packageName, string packageVersion, int timeout) + { + _remoteServices = remoteServices; + _packageName = packageName; + _packageVersion = packageVersion; + _timeout = timeout; + } + internal override void Start() + { + RequestCount++; + _steps = ESteps.DownloadPackageHashFile; + } + internal override void Update() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.DownloadPackageHashFile) + { + if (_queryRemotePackageHashOp == null) + { + _queryRemotePackageHashOp = new QueryRemotePackageHashOperation(_remoteServices, _packageName, _packageVersion, _timeout); + OperationSystem.StartOperation(_queryRemotePackageHashOp); + } + + if (_queryRemotePackageHashOp.IsDone == false) + return; + + if (_queryRemotePackageHashOp.Status == EOperationStatus.Succeed) + { + _steps = ESteps.DownloadManifestFile; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _queryRemotePackageHashOp.Error; + } + } + + if (_steps == ESteps.DownloadManifestFile) + { + if (_downloader == null) + { + string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_packageName, _packageVersion); + string webURL = GetDownloadRequestURL(fileName); + YooLogger.Log($"Beginning to download manifest file : {webURL}"); + _downloader = new UnityWebDataRequester(); + _downloader.SendRequest(webURL, _timeout); + } + + _downloader.CheckTimeout(); + if (_downloader.IsDone() == false) + return; + + if (_downloader.HasError()) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _downloader.GetError(); + } + else + { + _fileData = _downloader.GetData(); + _steps = ESteps.VerifyFileHash; + } + + _downloader.Dispose(); + } + + if (_steps == ESteps.VerifyFileHash) + { + string fileHash = HashUtility.BytesMD5(_fileData); + if (fileHash != _queryRemotePackageHashOp.PackageHash) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = "Failed to verify remote manifest file hash !"; + } + else + { + _deserializer = new DeserializeManifestOperation(_fileData); + OperationSystem.StartOperation(_deserializer); + _steps = ESteps.CheckDeserializeManifest; + } + } + + if (_steps == ESteps.CheckDeserializeManifest) + { + Progress = _deserializer.Progress; + if (_deserializer.IsDone == false) + return; + + if (_deserializer.Status == EOperationStatus.Succeed) + { + Manifest = _deserializer.Manifest; + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _deserializer.Error; + } + } + } + + private string GetDownloadRequestURL(string fileName) + { + // 轮流返回请求地址 + if (RequestCount % 2 == 0) + return _remoteServices.GetRemoteFallbackURL(fileName); + else + return _remoteServices.GetRemoteMainURL(fileName); + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs.meta b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs.meta new file mode 100644 index 0000000..8bd9488 --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/LoadRemoteManifestOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 486ce3e7ad16f2948a36d49ecabd76b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs new file mode 100644 index 0000000..e885358 --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs @@ -0,0 +1,99 @@ + +namespace YooAsset +{ + internal class QueryRemotePackageHashOperation : AsyncOperationBase + { + private enum ESteps + { + None, + DownloadPackageHash, + Done, + } + + private static int RequestCount = 0; + private readonly IRemoteServices _remoteServices; + private readonly string _packageName; + private readonly string _packageVersion; + private readonly int _timeout; + private UnityWebDataRequester _downloader; + private ESteps _steps = ESteps.None; + + /// + /// 包裹哈希值 + /// + public string PackageHash { private set; get; } + + + public QueryRemotePackageHashOperation(IRemoteServices remoteServices, string packageName, string packageVersion, int timeout) + { + _remoteServices = remoteServices; + _packageName = packageName; + _packageVersion = packageVersion; + _timeout = timeout; + } + internal override void Start() + { + RequestCount++; + _steps = ESteps.DownloadPackageHash; + } + internal override void Update() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.DownloadPackageHash) + { + if (_downloader == null) + { + string fileName = YooAssetSettingsData.GetPackageHashFileName(_packageName, _packageVersion); + string webURL = GetPackageHashRequestURL(fileName); + YooLogger.Log($"Beginning to request package hash : {webURL}"); + _downloader = new UnityWebDataRequester(); + _downloader.SendRequest(webURL, _timeout); + } + + Progress = _downloader.Progress(); + _downloader.CheckTimeout(); + if (_downloader.IsDone() == false) + return; + + if (_downloader.HasError()) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _downloader.GetError(); + } + else + { + PackageHash = _downloader.GetText(); + if (string.IsNullOrEmpty(PackageHash)) + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = $"Remote package hash is empty : {_downloader.URL}"; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + } + + _downloader.Dispose(); + } + } + + private string GetPackageHashRequestURL(string fileName) + { + string url; + + // 轮流返回请求地址 + if (RequestCount % 2 == 0) + url = _remoteServices.GetRemoteFallbackURL(fileName); + else + url = _remoteServices.GetRemoteMainURL(fileName); + + return url; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs.meta b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs.meta new file mode 100644 index 0000000..c2e3a61 --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageHashOperation.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ef77260f58172dd42ad10cfb862b78ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageVersionOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageVersionOperation.cs index ff7e93f..7fbb043 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageVersionOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/Internal/QueryRemotePackageVersionOperation.cs @@ -1,5 +1,4 @@ -using System.IO; - + namespace YooAsset { internal class QueryRemotePackageVersionOperation : AsyncOperationBase @@ -54,6 +53,7 @@ namespace YooAsset } Progress = _downloader.Progress(); + _downloader.CheckTimeout(); if (_downloader.IsDone() == false) return; diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/PreDownloadContentOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/PreDownloadContentOperation.cs index 1e21d09..2ca5047 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/PreDownloadContentOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/PreDownloadContentOperation.cs @@ -286,4 +286,14 @@ namespace YooAsset return operation; } } + internal class WebPlayModePreDownloadContentOperation : PreDownloadContentOperation + { + internal override void Start() + { + Status = EOperationStatus.Succeed; + } + internal override void Update() + { + } + } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageManifestOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageManifestOperation.cs index 2e11d41..3a31381 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageManifestOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageManifestOperation.cs @@ -184,9 +184,87 @@ namespace YooAsset } } - public override void SavePackageVersion() + public override void SavePackageVersion() { _impl.FlushManifestVersionFile(); } } + + /// + /// WebGL模式的更新清单操作 + /// + internal sealed class WebPlayModeUpdatePackageManifestOperation : UpdatePackageManifestOperation + { + private enum ESteps + { + None, + CheckActiveManifest, + LoadRemoteManifest, + Done, + } + + private readonly WebPlayModeImpl _impl; + private readonly string _packageName; + private readonly string _packageVersion; + private readonly int _timeout; + private LoadRemoteManifestOperation _loadCacheManifestOp; + private ESteps _steps = ESteps.None; + + + internal WebPlayModeUpdatePackageManifestOperation(WebPlayModeImpl impl, string packageName, string packageVersion, int timeout) + { + _impl = impl; + _packageName = packageName; + _packageVersion = packageVersion; + _timeout = timeout; + } + internal override void Start() + { + _steps = ESteps.CheckActiveManifest; + } + internal override void Update() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.CheckActiveManifest) + { + // 检测当前激活的清单对象 + if (_impl.ActiveManifest != null && _impl.ActiveManifest.PackageVersion == _packageVersion) + { + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + else + { + _steps = ESteps.LoadRemoteManifest; + } + } + + if (_steps == ESteps.LoadRemoteManifest) + { + if (_loadCacheManifestOp == null) + { + _loadCacheManifestOp = new LoadRemoteManifestOperation(_impl.RemoteServices, _packageName, _packageVersion, _timeout); + OperationSystem.StartOperation(_loadCacheManifestOp); + } + + if (_loadCacheManifestOp.IsDone == false) + return; + + if (_loadCacheManifestOp.Status == EOperationStatus.Succeed) + { + _impl.ActiveManifest = _loadCacheManifestOp.Manifest; + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _loadCacheManifestOp.Error; + } + } + } + } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageVersionOperation.cs b/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageVersionOperation.cs index 7430138..a92c2c2 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageVersionOperation.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/Operations/UpdatePackageVersionOperation.cs @@ -104,4 +104,66 @@ namespace YooAsset } } } + + /// + /// WebGL模式的请求远端包裹的最新版本 + /// + internal sealed class WebPlayModeUpdatePackageVersionOperation : UpdatePackageVersionOperation + { + private enum ESteps + { + None, + QueryRemotePackageVersion, + Done, + } + + private readonly WebPlayModeImpl _impl; + private readonly string _packageName; + private readonly bool _appendTimeTicks; + private readonly int _timeout; + private QueryRemotePackageVersionOperation _queryRemotePackageVersionOp; + private ESteps _steps = ESteps.None; + + internal WebPlayModeUpdatePackageVersionOperation(WebPlayModeImpl impl, string packageName, bool appendTimeTicks, int timeout) + { + _impl = impl; + _packageName = packageName; + _appendTimeTicks = appendTimeTicks; + _timeout = timeout; + } + internal override void Start() + { + _steps = ESteps.QueryRemotePackageVersion; + } + internal override void Update() + { + if (_steps == ESteps.None || _steps == ESteps.Done) + return; + + if (_steps == ESteps.QueryRemotePackageVersion) + { + if (_queryRemotePackageVersionOp == null) + { + _queryRemotePackageVersionOp = new QueryRemotePackageVersionOperation(_impl.RemoteServices, _packageName, _appendTimeTicks, _timeout); + OperationSystem.StartOperation(_queryRemotePackageVersionOp); + } + + if (_queryRemotePackageVersionOp.IsDone == false) + return; + + if (_queryRemotePackageVersionOp.Status == EOperationStatus.Succeed) + { + PackageVersion = _queryRemotePackageVersionOp.PackageVersion; + _steps = ESteps.Done; + Status = EOperationStatus.Succeed; + } + else + { + _steps = ESteps.Done; + Status = EOperationStatus.Failed; + Error = _queryRemotePackageVersionOp.Error; + } + } + } + } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/PackageBundle.cs b/Assets/YooAsset/Runtime/PackageSystem/PackageBundle.cs index 02addff..819eafd 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/PackageBundle.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/PackageBundle.cs @@ -11,6 +11,11 @@ namespace YooAsset /// public string BundleName; + /// + /// Unity引擎生成的CRC + /// + public uint UnityCRC; + /// /// 文件哈希值 /// diff --git a/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs b/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs new file mode 100644 index 0000000..cd07354 --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class WebPlayModeImpl : IPlayModeServices, IBundleServices + { + private PackageManifest _activeManifest; + + // 参数相关 + private string _packageName; + private IQueryServices _queryServices; + private IRemoteServices _remoteServices; + + public IRemoteServices RemoteServices + { + get { return _remoteServices; } + } + + /// + /// 异步初始化 + /// + public InitializationOperation InitializeAsync(string packageName, IQueryServices queryServices, IRemoteServices remoteServices) + { + _packageName = packageName; + _queryServices = queryServices; + _remoteServices = remoteServices; + + var operation = new WebPlayModeInitializationOperation(this, packageName); + OperationSystem.StartOperation(operation); + return operation; + } + + // 下载相关 + private BundleInfo ConvertToDownloadInfo(PackageBundle packageBundle) + { + string remoteMainURL = _remoteServices.GetRemoteMainURL(packageBundle.FileName); + string remoteFallbackURL = _remoteServices.GetRemoteFallbackURL(packageBundle.FileName); + BundleInfo bundleInfo = new BundleInfo(packageBundle, BundleInfo.ELoadMode.LoadFromRemote, remoteMainURL, remoteFallbackURL); + return bundleInfo; + } + + #region IPlayModeServices接口 + public PackageManifest ActiveManifest + { + set + { + _activeManifest = value; + } + get + { + return _activeManifest; + } + } + public void FlushManifestVersionFile() + { + } + + private bool IsBuildinPackageBundle(PackageBundle packageBundle) + { + return _queryServices.QueryStreamingAssets(_packageName, packageBundle.FileName); + } + + UpdatePackageVersionOperation IPlayModeServices.UpdatePackageVersionAsync(bool appendTimeTicks, int timeout) + { + var operation = new WebPlayModeUpdatePackageVersionOperation(this, _packageName, appendTimeTicks, timeout); + OperationSystem.StartOperation(operation); + return operation; + } + UpdatePackageManifestOperation IPlayModeServices.UpdatePackageManifestAsync(string packageVersion, bool autoSaveVersion, int timeout) + { + var operation = new WebPlayModeUpdatePackageManifestOperation(this, _packageName, packageVersion, timeout); + OperationSystem.StartOperation(operation); + return operation; + } + PreDownloadContentOperation IPlayModeServices.PreDownloadContentAsync(string packageVersion, int timeout) + { + var operation = new WebPlayModePreDownloadContentOperation(); + OperationSystem.StartOperation(operation); + return operation; + } + + ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout) + { + return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout); + } + ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByTags(string[] tags, int downloadingMaxNumber, int failedTryAgain, int timeout) + { + return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout); + } + ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByPaths(AssetInfo[] assetInfos, int downloadingMaxNumber, int failedTryAgain, int timeout) + { + return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout); + } + + ResourceUnpackerOperation IPlayModeServices.CreateResourceUnpackerByAll(int upackingMaxNumber, int failedTryAgain, int timeout) + { + return ResourceUnpackerOperation.CreateEmptyUnpacker(upackingMaxNumber, failedTryAgain, timeout); + } + ResourceUnpackerOperation IPlayModeServices.CreateResourceUnpackerByTags(string[] tags, int upackingMaxNumber, int failedTryAgain, int timeout) + { + return ResourceUnpackerOperation.CreateEmptyUnpacker(upackingMaxNumber, failedTryAgain, timeout); + } + #endregion + + #region IBundleServices接口 + private BundleInfo CreateBundleInfo(PackageBundle packageBundle) + { + if (packageBundle == null) + throw new Exception("Should never get here !"); + + // 查询APP资源 + if (IsBuildinPackageBundle(packageBundle)) + { + BundleInfo bundleInfo = new BundleInfo(packageBundle, BundleInfo.ELoadMode.LoadFromStreaming); + return bundleInfo; + } + + // 从服务端下载 + return ConvertToDownloadInfo(packageBundle); + } + BundleInfo IBundleServices.GetBundleInfo(AssetInfo assetInfo) + { + if (assetInfo.IsInvalid) + throw new Exception("Should never get here !"); + + // 注意:如果清单里未找到资源包会抛出异常! + var packageBundle = _activeManifest.GetMainPackageBundle(assetInfo.AssetPath); + return CreateBundleInfo(packageBundle); + } + BundleInfo[] IBundleServices.GetAllDependBundleInfos(AssetInfo assetInfo) + { + if (assetInfo.IsInvalid) + throw new Exception("Should never get here !"); + + // 注意:如果清单里未找到资源包会抛出异常! + var depends = _activeManifest.GetAllDependencies(assetInfo.AssetPath); + List result = new List(depends.Length); + foreach (var packageBundle in depends) + { + BundleInfo bundleInfo = CreateBundleInfo(packageBundle); + result.Add(bundleInfo); + } + return result.ToArray(); + } + string IBundleServices.GetBundleName(int bundleID) + { + return _activeManifest.GetBundleName(bundleID); + } + bool IBundleServices.IsServicesValid() + { + return _activeManifest != null; + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs.meta b/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs.meta new file mode 100644 index 0000000..a4b003a --- /dev/null +++ b/Assets/YooAsset/Runtime/PackageSystem/PlayMode/WebPlayModeImpl.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fa7d8823ee040534db067e6e2c254267 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PackageSystem/ResourcePackage.cs b/Assets/YooAsset/Runtime/PackageSystem/ResourcePackage.cs index 5aba8fe..c6303c9 100644 --- a/Assets/YooAsset/Runtime/PackageSystem/ResourcePackage.cs +++ b/Assets/YooAsset/Runtime/PackageSystem/ResourcePackage.cs @@ -126,6 +126,22 @@ namespace YooAsset initializeParameters.RemoteServices ); } + else if (_playMode == EPlayMode.WebPlayMode) + { + var webPlayModeImpl = new WebPlayModeImpl(); + _bundleServices = webPlayModeImpl; + _playModeServices = webPlayModeImpl; + _assetSystemImpl.Initialize(PackageName, false, + parameters.LoadingMaxTimeSlice, parameters.DownloadFailedTryAgain, + parameters.DecryptionServices, _bundleServices); + + var initializeParameters = parameters as WebPlayModeParameters; + initializeOperation = webPlayModeImpl.InitializeAsync( + PackageName, + initializeParameters.QueryServices, + initializeParameters.RemoteServices + ); + } else { throw new NotImplementedException(); @@ -184,9 +200,19 @@ namespace YooAsset _playMode = EPlayMode.OfflinePlayMode; else if (parameters is HostPlayModeParameters) _playMode = EPlayMode.HostPlayMode; + else if (parameters is WebPlayModeParameters) + _playMode = EPlayMode.WebPlayMode; else throw new NotImplementedException(); + // 检测运行平台 + if (_playMode == EPlayMode.HostPlayMode || _playMode == EPlayMode.OfflinePlayMode) + { +#if UNITY_WEBGL + throw new Exception($"WebGL plateform not support : {_playMode} ! Please use {nameof(EPlayMode.WebPlayMode)}"); +#endif + } + // 检测参数范围 if (parameters.LoadingMaxTimeSlice < 10) { diff --git a/Assets/YooAsset/Runtime/Settings/YooAssetSettings.cs b/Assets/YooAsset/Runtime/Settings/YooAssetSettings.cs index 69118b3..559bc39 100644 --- a/Assets/YooAsset/Runtime/Settings/YooAssetSettings.cs +++ b/Assets/YooAsset/Runtime/Settings/YooAssetSettings.cs @@ -24,7 +24,7 @@ namespace YooAsset /// /// 清单文件格式版本 /// - public const string ManifestFileVersion = "1.4.17"; + public const string ManifestFileVersion = "1.5.2"; ///