diff --git a/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs b/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs new file mode 100644 index 0000000..5144acd --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Threading; + +namespace YooAsset +{ + /// + /// 本地缓存文件验证器 + /// + internal abstract class PatchCacheVerifier + { + public abstract bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate); + public abstract bool UpdateVerifier(); + public abstract float GetVerifierProgress(); + + public int VerifySuccessCount { protected set; get; } = 0; + public int VerifyFailCount { protected set; get; } = 0; + } + + /// + /// 本地缓存文件验证器(线程版) + /// + internal class PatchCacheVerifierWithThread : PatchCacheVerifier + { + private class ThreadInfo + { + public bool Result = false; + public string FilePath { private set; get; } + public PatchBundle Bundle { private set; get; } + public ThreadInfo(string filePath, PatchBundle bundle) + { + FilePath = filePath; + Bundle = bundle; + } + } + + private readonly ThreadSyncContext _syncContext = new ThreadSyncContext(); + private readonly List _waitingList = new List(1000); + private readonly List _verifyingList = new List(100); + private int _verifyMaxNum; + private int _verifyTotalCount; + + public override bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate) + { + // 遍历所有文件然后验证并缓存合法文件 + foreach (var patchBundle in localPatchManifest.BundleList) + { + // 忽略缓存文件 + if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash)) + continue; + + // 忽略APP资源 + // 注意:如果是APP资源并且哈希值相同,则不需要下载 + if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle)) + { + if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash) + continue; + } + + // 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性 + if (weaklyUpdate) + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + if (File.Exists(filePath)) + _waitingList.Add(patchBundle); + else + return false; + } + else + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + if (File.Exists(filePath)) + _waitingList.Add(patchBundle); + } + } + + // 设置同时验证的最大数 + ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads); + YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}"); + _verifyMaxNum = Math.Min(workerThreads, ioThreads); + _verifyTotalCount = _waitingList.Count; + if (_verifyMaxNum < 1) + _verifyMaxNum = 1; + return true; + } + public override bool UpdateVerifier() + { + _syncContext.Update(); + + if (_waitingList.Count == 0 && _verifyingList.Count == 0) + return true; + + if (_verifyingList.Count >= _verifyMaxNum) + return false; + + for (int i = _waitingList.Count - 1; i >= 0; i--) + { + if (_verifyingList.Count >= _verifyMaxNum) + break; + + var patchBundle = _waitingList[i]; + if (VerifyFile(patchBundle)) + { + _waitingList.RemoveAt(i); + _verifyingList.Add(patchBundle); + } + else + { + YooLogger.Warning("The thread pool is failed queued."); + break; + } + } + + return false; + } + public override float GetVerifierProgress() + { + if (_verifyTotalCount == 0) + return 1f; + return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount; + } + + private bool VerifyFile(PatchBundle patchBundle) + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + ThreadInfo info = new ThreadInfo(filePath, patchBundle); + return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), info); + } + private void VerifyInThread(object infoObj) + { + ThreadInfo info = (ThreadInfo)infoObj; + info.Result = DownloadSystem.CheckContentIntegrity(info.FilePath, info.Bundle.FileSize, info.Bundle.FileCRC); + _syncContext.Post(VerifyCallback, info); + } + private void VerifyCallback(object obj) + { + ThreadInfo info = (ThreadInfo)obj; + if (info.Result) + { + VerifySuccessCount++; + DownloadSystem.CacheVerifyFile(info.Bundle.FileHash, info.Bundle.FileName); + } + else + { + VerifyFailCount++; + + // NOTE:不期望删除断点续传的资源文件 + /* + YooLogger.Warning($"Failed to verify file : {info.FilePath}"); + if (File.Exists(info.FilePath)) + File.Delete(info.FilePath); + */ + } + _verifyingList.Remove(info.Bundle); + } + } + + /// + /// 本地缓存文件验证器(非线程版) + /// + internal class PatchCacheVerifierWithoutThread : PatchCacheVerifier + { + private readonly List _waitingList = new List(1000); + private readonly List _verifyingList = new List(100); + private int _verifyMaxNum; + private int _verifyTotalCount; + + public override bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate) + { + // 遍历所有文件然后验证并缓存合法文件 + foreach (var patchBundle in localPatchManifest.BundleList) + { + // 忽略缓存文件 + if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash)) + continue; + + // 忽略APP资源 + // 注意:如果是APP资源并且哈希值相同,则不需要下载 + if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle)) + { + if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash) + continue; + } + + // 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性 + if (weaklyUpdate) + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + if (File.Exists(filePath)) + _waitingList.Add(patchBundle); + else + return false; + } + else + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + if (File.Exists(filePath)) + _waitingList.Add(patchBundle); + } + } + + // 设置同时验证的最大数 + _verifyMaxNum = 32; + _verifyTotalCount = _waitingList.Count; + return true; + } + public override bool UpdateVerifier() + { + if (_waitingList.Count == 0 && _verifyingList.Count == 0) + return true; + + for (int i = _waitingList.Count - 1; i >= 0; i--) + { + if (_verifyingList.Count >= _verifyMaxNum) + break; + + var patchBundle = _waitingList[i]; + VerifyFile(patchBundle); + _waitingList.RemoveAt(i); + _verifyingList.Add(patchBundle); + } + + _verifyingList.Clear(); + return false; + } + public override float GetVerifierProgress() + { + if (_verifyTotalCount == 0) + return 1f; + return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount; + } + + private void VerifyFile(PatchBundle patchBundle) + { + string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); + bool result = DownloadSystem.CheckContentIntegrity(filePath, patchBundle.FileSize, patchBundle.FileCRC); + if (result) + { + VerifySuccessCount++; + DownloadSystem.CacheVerifyFile(patchBundle.FileHash, patchBundle.FileName); + } + else + { + VerifyFailCount++; + + // NOTE:不期望删除断点续传的资源文件 + /* + YooLogger.Warning($"Failed to verify file : {info.FilePath}"); + if (File.Exists(info.FilePath)) + File.Delete(info.FilePath); + */ + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs.meta new file mode 100644 index 0000000..db73986 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/PatchCacheVerifier.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 223f217ed81460541b5ea7eb7a7d82a4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs index 9d5e682..7d9aa76 100644 --- a/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs +++ b/Assets/YooAsset/Runtime/PatchSystem/Operations/UpdateManifestOperation.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Threading; namespace YooAsset { @@ -69,7 +68,7 @@ namespace YooAsset private ESteps _steps = ESteps.None; private UnityWebDataRequester _downloader1; private UnityWebDataRequester _downloader2; - private VerifyManager _verifyManager = new VerifyManager(); + private PatchCacheVerifier _patchCacheVerifier; private float _verifyTime; internal HostPlayModeUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion, int timeout) @@ -77,6 +76,12 @@ namespace YooAsset _impl = impl; _resourceVersion = resourceVersion; _timeout = timeout; + +#if UNITY_WEBGL + _patchCacheVerifier = new PatchCacheVerifierWithoutThread(); +#else + _patchCacheVerifier = new PatchCacheVerifierWithThread(); +#endif } internal override void Start() { @@ -172,20 +177,20 @@ namespace YooAsset if (_steps == ESteps.InitVerifyingCache) { - _verifyManager.InitVerifyingCache(_impl.AppPatchManifest, _impl.LocalPatchManifest, false); + _patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, false); _verifyTime = UnityEngine.Time.realtimeSinceStartup; _steps = ESteps.UpdateVerifyingCache; } if (_steps == ESteps.UpdateVerifyingCache) { - Progress = _verifyManager.GetVerifyProgress(); - if (_verifyManager.UpdateVerifyingCache()) + Progress = _patchCacheVerifier.GetVerifierProgress(); + if (_patchCacheVerifier.UpdateVerifier()) { _steps = ESteps.Done; Status = EOperationStatus.Succeed; float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime; - YooLogger.Log($"Verify result : Success {_verifyManager.VerifySuccessCount}, Fail {_verifyManager.VerifyFailCount}, Elapsed time {costTime} seconds"); + YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds"); } } } @@ -268,13 +273,19 @@ namespace YooAsset private readonly HostPlayModeImpl _impl; private readonly int _resourceVersion; private ESteps _steps = ESteps.None; - private VerifyManager _verifyManager = new VerifyManager(); + private PatchCacheVerifier _patchCacheVerifier; private float _verifyTime; internal HostPlayModeWeaklyUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion) { _impl = impl; _resourceVersion = resourceVersion; + +#if UNITY_WEBGL + _patchCacheVerifier = new PatchCacheVerifierWithoutThread(); +#else + _patchCacheVerifier = new PatchCacheVerifierWithThread(); +#endif } internal override void Start() { @@ -293,7 +304,7 @@ namespace YooAsset if (_steps == ESteps.InitVerifyingCache) { - if (_verifyManager.InitVerifyingCache(_impl.AppPatchManifest, _impl.LocalPatchManifest, true)) + if (_patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, true)) { _verifyTime = UnityEngine.Time.realtimeSinceStartup; _steps = ESteps.UpdateVerifyingCache; @@ -308,12 +319,12 @@ namespace YooAsset if (_steps == ESteps.UpdateVerifyingCache) { - Progress = _verifyManager.GetVerifyProgress(); - if (_verifyManager.UpdateVerifyingCache()) + Progress = _patchCacheVerifier.GetVerifierProgress(); + if (_patchCacheVerifier.UpdateVerifier()) { float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime; - YooLogger.Log($"Verify result : Success {_verifyManager.VerifySuccessCount}, Fail {_verifyManager.VerifyFailCount}, Elapsed time {costTime} seconds"); - if (_verifyManager.VerifyFailCount > 0) + YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds"); + if (_patchCacheVerifier.VerifyFailCount > 0) { _steps = ESteps.Done; Status = EOperationStatus.Failed; @@ -344,143 +355,4 @@ namespace YooAsset } } } - - /// - /// 本地缓存文件验证管理器 - /// - internal class VerifyManager - { - private class ThreadInfo - { - public bool Result = false; - public string FilePath { private set; get; } - public PatchBundle Bundle { private set; get; } - public ThreadInfo(string filePath, PatchBundle bundle) - { - FilePath = filePath; - Bundle = bundle; - } - } - - private readonly List _waitingList = new List(1000); - private readonly List _verifyingList = new List(100); - private readonly ThreadSyncContext _syncContext = new ThreadSyncContext(); - private int _verifyMaxNum = 32; - private int _verifyTotalCount = 0; - - public int VerifySuccessCount { private set; get; } = 0; - public int VerifyFailCount { private set; get; } = 0; - - public bool InitVerifyingCache(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate) - { - // 遍历所有文件然后验证并缓存合法文件 - foreach (var patchBundle in localPatchManifest.BundleList) - { - // 忽略缓存文件 - if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash)) - continue; - - // 忽略APP资源 - // 注意:如果是APP资源并且哈希值相同,则不需要下载 - if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle)) - { - if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash) - continue; - } - - // 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性 - if (weaklyUpdate) - { - string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); - if (File.Exists(filePath)) - _waitingList.Add(patchBundle); - else - return false; - } - else - { - string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); - if (File.Exists(filePath)) - _waitingList.Add(patchBundle); - } - } - - // 设置同时验证的最大数 - ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads); - YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}"); - _verifyMaxNum = Math.Min(workerThreads, ioThreads); - _verifyTotalCount = _waitingList.Count; - return true; - } - public bool UpdateVerifyingCache() - { - _syncContext.Update(); - - if (_waitingList.Count == 0 && _verifyingList.Count == 0) - return true; - - if (_verifyingList.Count >= _verifyMaxNum) - return false; - - for (int i = _waitingList.Count - 1; i >= 0; i--) - { - if (_verifyingList.Count >= _verifyMaxNum) - break; - - var patchBundle = _waitingList[i]; - if (RunThread(patchBundle)) - { - _waitingList.RemoveAt(i); - _verifyingList.Add(patchBundle); - } - else - { - YooLogger.Warning("The thread pool is failed queued."); - break; - } - } - - return false; - } - public float GetVerifyProgress() - { - if (_verifyTotalCount == 0) - return 1f; - return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount; - } - - private bool RunThread(PatchBundle patchBundle) - { - string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName); - ThreadInfo info = new ThreadInfo(filePath, patchBundle); - return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), info); - } - private void VerifyInThread(object infoObj) - { - ThreadInfo info = (ThreadInfo)infoObj; - info.Result = DownloadSystem.CheckContentIntegrity(info.FilePath, info.Bundle.FileSize, info.Bundle.FileCRC); - _syncContext.Post(VerifyCallback, info); - } - private void VerifyCallback(object obj) - { - ThreadInfo info = (ThreadInfo)obj; - if (info.Result) - { - VerifySuccessCount++; - DownloadSystem.CacheVerifyFile(info.Bundle.FileHash, info.Bundle.FileName); - } - else - { - VerifyFailCount++; - - // NOTE:不期望删除断点续传的资源文件 - /* - YooLogger.Warning($"Failed to verify file : {info.FilePath}"); - if (File.Exists(info.FilePath)) - File.Delete(info.FilePath); - */ - } - _verifyingList.Remove(info.Bundle); - } - } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/YooAssets.cs b/Assets/YooAsset/Runtime/YooAssets.cs index f374276..6d5c3d1 100644 --- a/Assets/YooAsset/Runtime/YooAssets.cs +++ b/Assets/YooAsset/Runtime/YooAssets.cs @@ -100,10 +100,17 @@ namespace YooAsset /// public bool ClearCacheWhenDirty = false; +#if UNITY_WEBGL + /// + /// WEBGL模式不支持多线程下载 + /// + internal int BreakpointResumeFileSize = int.MaxValue; +#else /// /// 启用断点续传功能的文件大小 /// public int BreakpointResumeFileSize = int.MaxValue; +#endif /// /// 下载文件校验等级 @@ -194,12 +201,8 @@ namespace YooAsset // 初始化下载系统 if (_playMode == EPlayMode.HostPlayMode) { -#if UNITY_WEBGL - throw new Exception($"{EPlayMode.HostPlayMode} not supports WebGL platform !"); -#else var hostPlayModeParameters = parameters as HostPlayModeParameters; DownloadSystem.Initialize(hostPlayModeParameters.BreakpointResumeFileSize, hostPlayModeParameters.VerifyLevel); -#endif } // 初始化资源系统