DownloaderOperation.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. namespace YooAsset
  4. {
  5. public abstract class DownloaderOperation : AsyncOperationBase
  6. {
  7. private enum ESteps
  8. {
  9. None,
  10. Check,
  11. Loading,
  12. Done,
  13. }
  14. private const int MAX_LOADER_COUNT = 64;
  15. #region 委托定义
  16. /// <summary>
  17. /// 下载器结束
  18. /// </summary>
  19. public delegate void DownloaderFinish(DownloaderFinishData data);
  20. /// <summary>
  21. /// 下载进度更新
  22. /// </summary>
  23. public delegate void DownloadUpdate(DownloadUpdateData data);
  24. /// <summary>
  25. /// 下载发生错误
  26. /// </summary>
  27. public delegate void DownloadError(DownloadErrorData data);
  28. /// <summary>
  29. /// 开始下载某个文件
  30. /// </summary>
  31. public delegate void DownloadFileBegin(DownloadFileData data);
  32. #endregion
  33. private readonly string _packageName;
  34. private readonly int _downloadingMaxNumber;
  35. private readonly int _failedTryAgain;
  36. private readonly int _timeout;
  37. private readonly List<BundleInfo> _bundleInfoList;
  38. private readonly List<FSDownloadFileOperation> _downloaders = new List<FSDownloadFileOperation>(MAX_LOADER_COUNT);
  39. private readonly List<FSDownloadFileOperation> _removeList = new List<FSDownloadFileOperation>(MAX_LOADER_COUNT);
  40. private readonly List<FSDownloadFileOperation> _failedList = new List<FSDownloadFileOperation>(MAX_LOADER_COUNT);
  41. // 数据相关
  42. private bool _isPause = false;
  43. private long _lastDownloadBytes = 0;
  44. private int _lastDownloadCount = 0;
  45. private long _cachedDownloadBytes = 0;
  46. private int _cachedDownloadCount = 0;
  47. private ESteps _steps = ESteps.None;
  48. /// <summary>
  49. /// 统计的下载文件总数量
  50. /// </summary>
  51. public int TotalDownloadCount { private set; get; }
  52. /// <summary>
  53. /// 统计的下载文件的总大小
  54. /// </summary>
  55. public long TotalDownloadBytes { private set; get; }
  56. /// <summary>
  57. /// 当前已经完成的下载总数量
  58. /// </summary>
  59. public int CurrentDownloadCount
  60. {
  61. get { return _lastDownloadCount; }
  62. }
  63. /// <summary>
  64. /// 当前已经完成的下载总大小
  65. /// </summary>
  66. public long CurrentDownloadBytes
  67. {
  68. get { return _lastDownloadBytes; }
  69. }
  70. /// <summary>
  71. /// 当下载器结束(无论成功或失败)
  72. /// </summary>
  73. public DownloaderFinish DownloadFinishCallback { set; get; }
  74. /// <summary>
  75. /// 当下载进度发生变化
  76. /// </summary>
  77. public DownloadUpdate DownloadUpdateCallback { set; get; }
  78. /// <summary>
  79. /// 当下载器发生错误
  80. /// </summary>
  81. public DownloadError DownloadErrorCallback { set; get; }
  82. /// <summary>
  83. /// 当开始下载某个文件
  84. /// </summary>
  85. public DownloadFileBegin DownloadFileBeginCallback { set; get; }
  86. internal DownloaderOperation(string packageName, List<BundleInfo> downloadList, int downloadingMaxNumber, int failedTryAgain, int timeout)
  87. {
  88. _packageName = packageName;
  89. _bundleInfoList = downloadList;
  90. _downloadingMaxNumber = UnityEngine.Mathf.Clamp(downloadingMaxNumber, 1, MAX_LOADER_COUNT); ;
  91. _failedTryAgain = failedTryAgain;
  92. _timeout = timeout;
  93. // 设置包裹名称 (fix #210)
  94. SetPackageName(packageName);
  95. // 统计下载信息
  96. CalculatDownloaderInfo();
  97. }
  98. internal override void InternalStart()
  99. {
  100. YooLogger.Log($"Begine to download {TotalDownloadCount} files and {TotalDownloadBytes} bytes");
  101. _steps = ESteps.Check;
  102. }
  103. internal override void InternalUpdate()
  104. {
  105. if (_steps == ESteps.None || _steps == ESteps.Done)
  106. return;
  107. if (_steps == ESteps.Check)
  108. {
  109. if (_bundleInfoList == null)
  110. {
  111. _steps = ESteps.Done;
  112. Status = EOperationStatus.Failed;
  113. Error = "Download list is null.";
  114. }
  115. else
  116. {
  117. _steps = ESteps.Loading;
  118. }
  119. }
  120. if (_steps == ESteps.Loading)
  121. {
  122. // 检测下载器结果
  123. _removeList.Clear();
  124. long downloadBytes = _cachedDownloadBytes;
  125. foreach (var downloader in _downloaders)
  126. {
  127. downloader.UpdateOperation();
  128. downloadBytes += downloader.DownloadedBytes;
  129. if (downloader.IsDone == false)
  130. continue;
  131. // 检测是否下载失败
  132. if (downloader.Status != EOperationStatus.Succeed)
  133. {
  134. _removeList.Add(downloader);
  135. _failedList.Add(downloader);
  136. continue;
  137. }
  138. // 下载成功
  139. _removeList.Add(downloader);
  140. _cachedDownloadCount++;
  141. _cachedDownloadBytes += downloader.DownloadedBytes;
  142. }
  143. // 移除已经完成的下载器(无论成功或失败)
  144. foreach (var downloader in _removeList)
  145. {
  146. _downloaders.Remove(downloader);
  147. }
  148. // 如果下载进度发生变化
  149. if (_lastDownloadBytes != downloadBytes || _lastDownloadCount != _cachedDownloadCount)
  150. {
  151. _lastDownloadBytes = downloadBytes;
  152. _lastDownloadCount = _cachedDownloadCount;
  153. Progress = (float)_lastDownloadBytes / TotalDownloadBytes;
  154. if (DownloadUpdateCallback != null)
  155. {
  156. var data = new DownloadUpdateData();
  157. data.PackageName = _packageName;
  158. data.Progress = Progress;
  159. data.TotalDownloadCount = TotalDownloadCount;
  160. data.CurrentDownloadCount = _lastDownloadCount;
  161. data.TotalDownloadBytes = TotalDownloadBytes;
  162. data.CurrentDownloadBytes = _lastDownloadBytes;
  163. DownloadUpdateCallback.Invoke(data);
  164. }
  165. }
  166. // 动态创建新的下载器到最大数量限制
  167. // 注意:如果期间有下载失败的文件,暂停动态创建下载器
  168. if (_bundleInfoList.Count > 0 && _failedList.Count == 0)
  169. {
  170. if (_isPause)
  171. return;
  172. if (_downloaders.Count < _downloadingMaxNumber)
  173. {
  174. int index = _bundleInfoList.Count - 1;
  175. var bundleInfo = _bundleInfoList[index];
  176. var downloader = bundleInfo.CreateDownloader(_failedTryAgain, _timeout);
  177. downloader.StartOperation();
  178. this.AddChildOperation(downloader);
  179. _downloaders.Add(downloader);
  180. _bundleInfoList.RemoveAt(index);
  181. if (DownloadFileBeginCallback != null)
  182. {
  183. var data = new DownloadFileData();
  184. data.PackageName = _packageName;
  185. data.FileName = bundleInfo.Bundle.BundleName;
  186. data.FileSize = bundleInfo.Bundle.FileSize;
  187. DownloadFileBeginCallback.Invoke(data);
  188. }
  189. }
  190. }
  191. // 下载结算
  192. if (_downloaders.Count == 0)
  193. {
  194. if (_failedList.Count > 0)
  195. {
  196. var failedDownloader = _failedList[0];
  197. string bundleName = failedDownloader.Bundle.BundleName;
  198. _steps = ESteps.Done;
  199. Status = EOperationStatus.Failed;
  200. Error = $"Failed to download file : {bundleName}";
  201. if (DownloadErrorCallback != null)
  202. {
  203. var data = new DownloadErrorData();
  204. data.PackageName = _packageName;
  205. data.FileName = bundleName;
  206. data.ErrorInfo = failedDownloader.Error;
  207. DownloadErrorCallback.Invoke(data);
  208. }
  209. if (DownloadFinishCallback != null)
  210. {
  211. var data = new DownloaderFinishData();
  212. data.PackageName = _packageName;
  213. data.Succeed = false;
  214. DownloadFinishCallback.Invoke(data);
  215. }
  216. }
  217. else
  218. {
  219. // 结算成功
  220. _steps = ESteps.Done;
  221. Status = EOperationStatus.Succeed;
  222. if (DownloadFinishCallback != null)
  223. {
  224. var data = new DownloaderFinishData();
  225. data.PackageName = _packageName;
  226. data.Succeed = true;
  227. DownloadFinishCallback.Invoke(data);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. private void CalculatDownloaderInfo()
  234. {
  235. if (_bundleInfoList != null)
  236. {
  237. TotalDownloadBytes = 0;
  238. TotalDownloadCount = _bundleInfoList.Count;
  239. foreach (var packageBundle in _bundleInfoList)
  240. {
  241. TotalDownloadBytes += packageBundle.Bundle.FileSize;
  242. }
  243. }
  244. else
  245. {
  246. TotalDownloadBytes = 0;
  247. TotalDownloadCount = 0;
  248. }
  249. }
  250. /// <summary>
  251. /// 合并其它下载器
  252. /// </summary>
  253. /// <param name="downloader">合并的下载器</param>
  254. public void Combine(DownloaderOperation downloader)
  255. {
  256. if (_packageName != downloader._packageName)
  257. {
  258. YooLogger.Error("The downloaders have different resource packages !");
  259. return;
  260. }
  261. if (Status != EOperationStatus.None)
  262. {
  263. YooLogger.Error("The downloader is running, can not combine with other downloader !");
  264. return;
  265. }
  266. HashSet<string> temper = new HashSet<string>();
  267. foreach (var bundleInfo in _bundleInfoList)
  268. {
  269. string combineGUID = bundleInfo.GetDownloadCombineGUID();
  270. if (temper.Contains(combineGUID) == false)
  271. {
  272. temper.Add(combineGUID);
  273. }
  274. }
  275. // 合并下载列表
  276. foreach (var bundleInfo in downloader._bundleInfoList)
  277. {
  278. string combineGUID = bundleInfo.GetDownloadCombineGUID();
  279. if (temper.Contains(combineGUID) == false)
  280. {
  281. _bundleInfoList.Add(bundleInfo);
  282. }
  283. }
  284. // 重新统计下载信息
  285. CalculatDownloaderInfo();
  286. }
  287. /// <summary>
  288. /// 开始下载
  289. /// </summary>
  290. public void BeginDownload()
  291. {
  292. if (_steps == ESteps.None)
  293. {
  294. OperationSystem.StartOperation(_packageName, this);
  295. }
  296. }
  297. /// <summary>
  298. /// 暂停下载
  299. /// </summary>
  300. public void PauseDownload()
  301. {
  302. _isPause = true;
  303. }
  304. /// <summary>
  305. /// 恢复下载
  306. /// </summary>
  307. public void ResumeDownload()
  308. {
  309. _isPause = false;
  310. }
  311. /// <summary>
  312. /// 取消下载
  313. /// </summary>
  314. public void CancelDownload()
  315. {
  316. if (_steps != ESteps.Done)
  317. {
  318. _steps = ESteps.Done;
  319. Status = EOperationStatus.Failed;
  320. Error = "User cancel.";
  321. foreach (var downloader in _downloaders)
  322. {
  323. downloader.Release();
  324. }
  325. }
  326. }
  327. }
  328. public sealed class ResourceDownloaderOperation : DownloaderOperation
  329. {
  330. internal ResourceDownloaderOperation(string packageName, List<BundleInfo> downloadList, int downloadingMaxNumber, int failedTryAgain, int timeout)
  331. : base(packageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout)
  332. {
  333. }
  334. /// <summary>
  335. /// 创建空的下载器
  336. /// </summary>
  337. internal static ResourceDownloaderOperation CreateEmptyDownloader(string packageName, int downloadingMaxNumber, int failedTryAgain, int timeout)
  338. {
  339. List<BundleInfo> downloadList = new List<BundleInfo>();
  340. var operation = new ResourceDownloaderOperation(packageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout);
  341. return operation;
  342. }
  343. }
  344. public sealed class ResourceUnpackerOperation : DownloaderOperation
  345. {
  346. internal ResourceUnpackerOperation(string packageName, List<BundleInfo> downloadList, int downloadingMaxNumber, int failedTryAgain, int timeout)
  347. : base(packageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout)
  348. {
  349. }
  350. /// <summary>
  351. /// 创建空的解压器
  352. /// </summary>
  353. internal static ResourceUnpackerOperation CreateEmptyUnpacker(string packageName, int upackingMaxNumber, int failedTryAgain, int timeout)
  354. {
  355. List<BundleInfo> downloadList = new List<BundleInfo>();
  356. var operation = new ResourceUnpackerOperation(packageName, downloadList, upackingMaxNumber, failedTryAgain, int.MaxValue);
  357. return operation;
  358. }
  359. }
  360. public sealed class ResourceImporterOperation : DownloaderOperation
  361. {
  362. internal ResourceImporterOperation(string packageName, List<BundleInfo> downloadList, int downloadingMaxNumber, int failedTryAgain, int timeout)
  363. : base(packageName, downloadList, downloadingMaxNumber, failedTryAgain, timeout)
  364. {
  365. }
  366. /// <summary>
  367. /// 创建空的导入器
  368. /// </summary>
  369. internal static ResourceImporterOperation CreateEmptyImporter(string packageName, int upackingMaxNumber, int failedTryAgain, int timeout)
  370. {
  371. List<BundleInfo> downloadList = new List<BundleInfo>();
  372. var operation = new ResourceImporterOperation(packageName, downloadList, upackingMaxNumber, failedTryAgain, int.MaxValue);
  373. return operation;
  374. }
  375. }
  376. }