Files
JJBB/Assets/Project/Script/AssetManagement/BundleDownload/BundleDownloadHub.cs

245 lines
8.4 KiB
C#
Raw Normal View History

2024-08-23 15:49:34 +08:00
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace AssetManagement
{
// 2020.01.04 - Blastom因为当前版本UnityWebRequest不支持下载https
// 现在下载部分无法重用AsyncLoadHub
// 快速解决方案是复制AsyncLoadHub 修改为支持BestHttp的流程。
public class BundleDownloadHub
{
// 失败后重新下载等待时间
public const float failWaitTime = 1f;
// 失败后重新下载次数
public const int maxRetryCount = 5;
// 后台失败后重新下载次数
public const int maxRetryLowPriority = 1;
// 重新下载优先度,该优先度高于任何其他级别
public const int retryPriority = 100;
public readonly List<BundleDownloader> activeUnits = new List<BundleDownloader>();
public readonly DownloaderQueue waitQueue = new DownloaderQueue();
protected AssetPathHub pathHub;
public List<AsyncLoadRule> rules;
public void StartBackGroundDownload()
{
var downloadList = pathHub.GetBackgroundDownloadList();
foreach (var download in downloadList)
LoadUnit(download.name, -1);
}
public event UnityAction<BundleDownloader> onComplete;
public void Init(AssetPathHub pathHub, List<AsyncLoadRule> rules)
{
this.pathHub = pathHub;
this.rules = rules;
this.rules.Sort((a, b) => a.priority.CompareTo(b.priority));
}
public BundleDownloader LoadUnit(string assetName, int priority = 0)
{
// 如果已经通过其他渠道下载,则直接返回当前下载的单位
var unit = activeUnits.Find(a => a.name == assetName);
if (unit == null)
{
var waitState = waitQueue.TrySetPriority(assetName, priority, out unit);
switch (waitState)
{
case SetPriorityResult.NoItem:
{
unit = new BundleDownloader();
unit.Init(pathHub, assetName, priority);
// 无需检查是否有重试延迟
if (CanActive(unit.priority))
ActivateUnit(unit);
else
waitQueue.AddInQueue(unit);
break;
}
case SetPriorityResult.NoUpdate:
// Nothing Happened
break;
case SetPriorityResult.Update:
if (CanActive(priority))
ActivateWaitUnits(1);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
else if (unit.priority < priority)
unit.priority = priority;
return unit;
}
// 注加载中的单位是不可停止的Unity机制限制
/// <summary>
/// 停止一个加载单位,返回是否可以停止
/// </summary>
public bool StopUnit(BundleDownloader unit)
{
var result = false;
if (waitQueue.RemoveInQueue(unit.name))
result = true;
else if (activeUnits.Find(a => a.name == unit.name) == null)
result = true;
return result;
}
// 仅仅在关闭App调用没有清理Unit会留下垃圾
public void StopAll()
{
for (var i = 0; i < activeUnits.Count; i++)
activeUnits[i].Dispose();
}
private void OnLoadComplete(BundleDownloader activeUnit)
{
activeUnit.onComplete -= OnLoadComplete;
activeUnits.Remove(activeUnit);
// 下载成功
if (string.IsNullOrEmpty(activeUnit.error))
{
if (activeUnit.failCount > 0)
Debug.LogWarning(string.Format("Successfully downloaded {0} at attempt {1}!", activeUnit.name, activeUnit.failCount + 1));
if (onComplete != null)
onComplete.Invoke(activeUnit);
}
// 重试次数超过上限
else
{
var fail = activeUnit.priority > 0
? activeUnit.failCount >= maxRetryCount
: activeUnit.failCount >= maxRetryLowPriority;
if (fail)
{
Debug.LogError(string.Format("Failed to download {0} after all attempts expired!",
activeUnit.name));
if (onComplete != null)
onComplete.Invoke(activeUnit);
}
else
// 重新尝试下载
{
activeUnit.SetRetry(Time.realtimeSinceStartup + failWaitTime);
activeUnit.priority = retryPriority;
activeUnit.onWaitUp += OnUnitWaitUp;
Debug.Log(string.Format("Trying to restart download for bundle {0}, attempt {1}!", activeUnit.name, activeUnit.failCount));
waitQueue.AddInQueue(activeUnit);
}
}
ActivateWaitUnits(1);
}
private void ActivateWaitUnits(int count)
{
var active = 0;
for (var i = waitQueue.list.Count - 1; i >= 0; i--)
{
var unit = waitQueue.list[i];
if (!CanActive(unit.priority))
break;
if (unit.downloadTimer == null)
{
active++;
waitQueue.list.RemoveAt(i);
ActivateUnit(unit);
if (active >= count)
break;
}
}
}
private void OnUnitWaitUp(BundleDownloader unit)
{
unit.onWaitUp -= OnUnitWaitUp;
ActivateWaitUnits(1);
}
private void ActivateUnit(BundleDownloader unit)
{
activeUnits.Add(unit);
unit.onComplete += OnLoadComplete;
unit.Load();
}
private bool CanActive(int priority)
{
return GetMaxActive(priority) > activeUnits.Count;
}
private int GetMaxActive(int priority)
{
var result = -1;
foreach (var rule in rules)
if (rule.priority > priority)
result = rule.maxActive;
return result;
}
}
public class DownloaderQueue
{
public readonly List<BundleDownloader> list = new List<BundleDownloader>();
public bool RemoveInQueue(string name)
{
var result = false;
for (var i = 0; i < list.Count; i++)
if (list[i].name == name)
{
result = true;
list.RemoveAt(i);
break;
}
return result;
}
public void AddInQueue(BundleDownloader actionUnit)
{
var add = true;
for (var i = 0; i < list.Count; i++)
if (list[i].priority >= actionUnit.priority)
{
add = false;
list.Insert(i, actionUnit);
break;
}
if (add)
list.Add(actionUnit);
}
public SetPriorityResult TrySetPriority(string name, int priority, out BundleDownloader unit)
{
unit = null;
var result = SetPriorityResult.NoItem;
for (var i = 0; i < list.Count; i++)
if (list[i].name == name)
{
unit = list[i];
if (priority > list[i].priority && i < list.Count - 1)
{
result = SetPriorityResult.Update;
var actionUnit = list[i];
actionUnit.priority = priority;
list.RemoveAt(i);
AddInQueue(actionUnit);
}
else
{
result = SetPriorityResult.NoUpdate;
}
break;
}
return result;
}
}
}