245 lines
8.4 KiB
C#
245 lines
8.4 KiB
C#
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;
|
||
}
|
||
}
|
||
} |