Files
2024-08-23 15:49:34 +08:00

245 lines
8.4 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}
}