diff --git a/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs new file mode 100644 index 00000000..726aa382 --- /dev/null +++ b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; + +namespace YooAsset +{ + internal class DefaultCacheDownloadCenter + { + private readonly DefaultCacheFileSystem _fileSystem; + protected readonly Dictionary _downloaders = new Dictionary(1000); + protected readonly List _removeDownloadList = new List(1000); + + public DefaultCacheDownloadCenter(DefaultCacheFileSystem fileSystem) + { + _fileSystem = fileSystem; + } + + /// + /// 更新下载中心 + /// + public void Update() + { + // 获取可移除的下载器集合 + _removeDownloadList.Clear(); + foreach (var valuePair in _downloaders) + { + var downloader = valuePair.Value; + + // 注意:主动终止引用计数为零的下载任务 + if (downloader.RefCount <= 0) + { + _removeDownloadList.Add(valuePair.Key); + downloader.SetAbort(); + continue; + } + + if (downloader.IsDone) + { + _removeDownloadList.Add(valuePair.Key); + continue; + } + } + + // 移除下载器 + foreach (var key in _removeDownloadList) + { + _downloaders.Remove(key); + } + + // 最大并发数检测 + int processCount = GetProcessingOperationCount(); + if (processCount != _downloaders.Count) + { + if (processCount < _fileSystem.DownloadMaxConcurrency) + { + int startCount = _fileSystem.DownloadMaxConcurrency - processCount; + if (startCount > _fileSystem.DownloadMaxRequestPerFrame) + startCount = _fileSystem.DownloadMaxRequestPerFrame; + + foreach (var operationPair in _downloaders) + { + var operation = operationPair.Value; + if (operation.Status == EOperationStatus.None) + { + OperationSystem.StartOperation(_fileSystem.PackageName, operation); + startCount--; + if (startCount <= 0) + break; + } + } + } + } + } + + /// + /// 创建下载任务 + /// + public FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadParam param) + { + // 查询旧的下载器 + if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader)) + { + oldDownloader.Reference(); + return oldDownloader; + } + + // 设置请求URL + bool importFile = false; + if (string.IsNullOrEmpty(param.ImportFilePath)) + { + param.MainURL = _fileSystem.RemoteServices.GetRemoteMainURL(bundle.FileName); + param.FallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(bundle.FileName); + } + else + { + // 注意:把本地文件路径指定为远端下载地址 + param.MainURL = DownloadSystemHelper.ConvertToWWWPath(param.ImportFilePath); + param.FallbackURL = param.MainURL; + importFile = true; + } + + // 创建新的下载器 + DefaultDownloadFileOperation newDownloader; + if (bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize) + { + newDownloader = new DownloadResumeFileOperation(_fileSystem, _fileSystem, bundle, param, _fileSystem.ResumeDownloadResponseCodes); + newDownloader.Reference(); + _downloaders.Add(bundle.BundleGUID, newDownloader); + } + else + { + newDownloader = new DownloadNormalFileOperation(_fileSystem, _fileSystem, bundle, param); + newDownloader.Reference(); + _downloaders.Add(bundle.BundleGUID, newDownloader); + } + + // 注意:导入文件不要限制并发!导入文件涉及到异步转同步,限制并发会导致主线程卡死风险! + if (importFile) + { + OperationSystem.StartOperation(_fileSystem.PackageName, newDownloader); + } + return newDownloader; + } + + /// + /// 获取正在进行中的下载器总数 + /// + private int GetProcessingOperationCount() + { + int count = 0; + foreach (var operationPair in _downloaders) + { + var operation = operationPair.Value; + if (operation.Status != EOperationStatus.None) + count++; + } + return count; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs.meta b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs.meta new file mode 100644 index 00000000..a7e85c28 --- /dev/null +++ b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheDownloadCenter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 768ef2df3433df245a26fec28d022e45 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheFileSystem.cs b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheFileSystem.cs index 6c5b63ab..26c146ea 100644 --- a/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheFileSystem.cs +++ b/Assets/YooAsset/Runtime/FileSystem/DefaultCacheFileSystem/DefaultCacheFileSystem.cs @@ -17,8 +17,7 @@ namespace YooAsset protected readonly Dictionary _dataFilePaths = new Dictionary(10000); protected readonly Dictionary _infoFilePaths = new Dictionary(10000); protected readonly Dictionary _tempFilePaths = new Dictionary(10000); - protected readonly Dictionary _downloaders = new Dictionary(1000); - protected readonly List _removeList = new List(1000); + protected DefaultCacheDownloadCenter _downloadCenter; protected string _packageRoot; protected string _cacheFileRoot; @@ -73,6 +72,16 @@ namespace YooAsset /// public bool RawFileBuildPipeline { private set; get; } = false; + /// + /// 自定义参数:最大并发连接数 + /// + public int DownloadMaxConcurrency { private set; get; } = int.MaxValue; + + /// + /// 自定义参数:每帧发起的最大请求数 + /// + public int DownloadMaxRequestPerFrame { private set; get; } = int.MaxValue; + /// /// 自定义参数:启用断点续传的最小尺寸 /// @@ -113,19 +122,19 @@ namespace YooAsset } public virtual FSClearCacheBundleFilesOperation ClearCacheBundleFilesAsync(PackageManifest manifest, string clearMode, object clearParam) { - if(clearMode == EFileClearMode.ClearAllBundleFiles.ToString()) + if (clearMode == EFileClearMode.ClearAllBundleFiles.ToString()) { var operation = new ClearAllCacheFilesOperation(this); OperationSystem.StartOperation(PackageName, operation); return operation; } - else if(clearMode == EFileClearMode.ClearUnusedBundleFiles.ToString()) + else if (clearMode == EFileClearMode.ClearUnusedBundleFiles.ToString()) { var operation = new ClearUnusedCacheFilesOperation(this, manifest); OperationSystem.StartOperation(PackageName, operation); return operation; } - else if(clearMode == EFileClearMode.ClearBundleFilesByTags.ToString()) + else if (clearMode == EFileClearMode.ClearBundleFilesByTags.ToString()) { var operation = new ClearCacheFilesByTagsOperaiton(this, manifest, clearParam); OperationSystem.StartOperation(PackageName, operation); @@ -141,44 +150,7 @@ namespace YooAsset } public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadParam param) { - // 查询旧的下载器 - if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader)) - { - oldDownloader.Reference(); - return oldDownloader; - } - - // 创建新的下载器 - { - if (string.IsNullOrEmpty(param.ImportFilePath)) - { - param.MainURL = RemoteServices.GetRemoteMainURL(bundle.FileName); - param.FallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName); - } - else - { - // 注意:把本地文件路径指定为远端下载地址 - param.MainURL = DownloadSystemHelper.ConvertToWWWPath(param.ImportFilePath); - param.FallbackURL = param.MainURL; - } - - if (bundle.FileSize >= ResumeDownloadMinimumSize) - { - var newDownloader = new DownloadResumeFileOperation(this, this, bundle, param, ResumeDownloadResponseCodes); - newDownloader.Reference(); - _downloaders.Add(bundle.BundleGUID, newDownloader); - OperationSystem.StartOperation(PackageName, newDownloader); - return newDownloader; - } - else - { - var newDownloader = new DownloadNormalFileOperation(this, this, bundle, param); - newDownloader.Reference(); - _downloaders.Add(bundle.BundleGUID, newDownloader); - OperationSystem.StartOperation(PackageName, newDownloader); - return newDownloader; - } - } + return _downloadCenter.DownloadFileAsync(bundle, param); } public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle) { @@ -233,6 +205,14 @@ namespace YooAsset { RawFileBuildPipeline = (bool)value; } + else if (name == FileSystemParametersDefine.DOWNLOAD_MAX_CONCURRENCY) + { + DownloadMaxConcurrency = (int)value; + } + else if (name == FileSystemParametersDefine.DOWNLOAD_MAX_REQUEST_PER_FRAME) + { + DownloadMaxRequestPerFrame = (int)value; + } else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE) { ResumeDownloadMinimumSize = (long)value; @@ -261,31 +241,11 @@ namespace YooAsset _cacheFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.SaveFilesFolderName); _tempFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.TempFilesFolderName); _manifestFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.ManifestFilesFolderName); + _downloadCenter = new DefaultCacheDownloadCenter(this); } public virtual void OnUpdate() { - _removeList.Clear(); - - foreach (var valuePair in _downloaders) - { - var downloader = valuePair.Value; - - // 注意:主动终止引用计数为零的下载任务 - if (downloader.RefCount <= 0) - { - _removeList.Add(valuePair.Key); - downloader.SetAbort(); - continue; - } - - if (downloader.IsDone) - _removeList.Add(valuePair.Key); - } - - foreach (var key in _removeList) - { - _downloaders.Remove(key); - } + _downloadCenter.Update(); } public virtual bool Belong(PackageBundle bundle) diff --git a/Assets/YooAsset/Runtime/FileSystem/FileSystemParametersDefine.cs b/Assets/YooAsset/Runtime/FileSystem/FileSystemParametersDefine.cs index 17f88812..079031c2 100644 --- a/Assets/YooAsset/Runtime/FileSystem/FileSystemParametersDefine.cs +++ b/Assets/YooAsset/Runtime/FileSystem/FileSystemParametersDefine.cs @@ -9,6 +9,8 @@ namespace YooAsset public const string APPEND_FILE_EXTENSION = "APPEND_FILE_EXTENSION"; public const string RAW_FILE_BUILD_PIPELINE = "RAW_FILE_BUILD_PIPELINE"; public const string DISABLE_UNITY_WEB_CACHE = "DISABLE_UNITY_WEB_CACHE"; + public const string DOWNLOAD_MAX_CONCURRENCY = "DOWNLOAD_MAX_CONCURRENCY"; + public const string DOWNLOAD_MAX_REQUEST_PER_FRAME = "DOWNLOAD_MAX_REQUEST_PER_FRAME"; public const string RESUME_DOWNLOAD_MINMUM_SIZE = "RESUME_DOWNLOAD_MINMUM_SIZE"; public const string RESUME_DOWNLOAD_RESPONSE_CODES = "RESUME_DOWNLOAD_RESPONSE_CODES"; public const string ASYNC_SIMULATE_MIN_FRAME = "ASYNC_SIMULATE_MIN_FRAME";