515 lines
19 KiB
C#
515 lines
19 KiB
C#
using asap.core;
|
||
using GameCore;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using UnityEngine;
|
||
using UnityEngine.AddressableAssets;
|
||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||
public interface ILoadResourceService
|
||
{
|
||
/// <summary>
|
||
/// 需要显示下载进度的并发下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
Task<bool> Load(string label);
|
||
/// <summary>
|
||
/// 需要显示下载进度的并发下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
Task<bool> Loads(List<string> label);
|
||
/// <summary>
|
||
/// 不需要知道任何下载情况的后台静默排队下载,资源包队列下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
void EnqueueBundleSilently(List<string> label);
|
||
/// <summary>
|
||
/// 登陆游戏预下载地图资源
|
||
/// </summary>
|
||
/// <param name="assetName"></param>
|
||
/// <param name="progress"></param>
|
||
/// <returns></returns>
|
||
Task<bool> Loading(List<string> assetName, System.Action<float> progress);
|
||
/// <summary>
|
||
/// 检查资源是否需要下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
Task<bool> CheckResource(string label);
|
||
/// <summary>
|
||
/// 后台排队下载,如果需要下载则排队下载等待 LoadEventResourceEvent 事件,否则直接返回true,传入 List 为组队列下载
|
||
/// </summary>
|
||
/// <param name="key"></param>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
Task<bool> CheckResourceLoadQueue(string key, List<string> label);
|
||
string curName { get; }
|
||
string oldName { get; }
|
||
}
|
||
public class LoadResourceData
|
||
{
|
||
public string name;
|
||
public AsyncOperationHandle downloadOp;
|
||
}
|
||
public struct LoadEventResourceEvent
|
||
{
|
||
public string key;
|
||
public List<string> label;
|
||
public bool isSucceeded;
|
||
}
|
||
public class LoadProgressEvent
|
||
{
|
||
public string name;
|
||
public float progress;
|
||
public float totalDownloadSize;
|
||
public bool isSucceeded;
|
||
public bool isFailed;
|
||
}
|
||
public class LoadResourceService : ILoadResourceService
|
||
{
|
||
[Inject]
|
||
public IConfig config { get; set; }
|
||
Dictionary<string, AsyncOperationHandle> loadResourceDatas = new Dictionary<string, AsyncOperationHandle>();
|
||
LinkedList<string> loadQueue = new LinkedList<string>();
|
||
LinkedList<LoadEventResourceEvent> loadEventQueue = new LinkedList<LoadEventResourceEvent>();
|
||
bool LoadEventResourceing = false;
|
||
Dictionary<string, bool> loadRecord = new Dictionary<string, bool>();
|
||
//检查资源是否需要更新
|
||
public string curName { get; private set; }
|
||
public string oldName { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 登陆游戏预下载地图资源
|
||
/// </summary>
|
||
/// <param name="assetName"></param>
|
||
/// <param name="progress"> 进度 </param>
|
||
/// <returns></returns>
|
||
public async Task<bool> Loading(List<string> assetName, System.Action<float> progress)
|
||
{
|
||
if (!loadResourceDatas.ContainsKey(assetName[0]))
|
||
{
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(assetName, Addressables.MergeMode.Union);
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
|
||
if (totalDownloadSize > 1024)
|
||
{
|
||
AsyncOperationHandle download = Addressables.DownloadDependenciesAsync(locations.Select(_ => _.PrimaryKey), Addressables.MergeMode.Union);
|
||
loadResourceDatas[assetName[0]] = download;
|
||
}
|
||
Addressables.Release(locationsOp);
|
||
}
|
||
bool result = true;
|
||
if (loadResourceDatas.ContainsKey(assetName[0]))
|
||
{
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("Preload_start"))
|
||
{
|
||
e.AddContent("expected_assets", string.Join(',', assetName));
|
||
}
|
||
#endif
|
||
float percent = 0;
|
||
LoadingScreen.SetLoadingInfo(LocalizationMgr.GetText("UI_ToastPanel_26"));
|
||
|
||
AsyncOperationHandle downloadOp = loadResourceDatas[assetName[0]];
|
||
DateTime startTime = DateTime.Now;
|
||
LoadingScreen.SetSubProgress(0, "");
|
||
LoadingScreen.ShowSubProgressBar();
|
||
while (!downloadOp.IsDone && downloadOp.Status != AsyncOperationStatus.Failed)
|
||
{
|
||
var downloadStatus = downloadOp.GetDownloadStatus();
|
||
if (downloadStatus.Percent > percent)
|
||
percent = downloadStatus.Percent;
|
||
progress?.Invoke(percent);
|
||
LoadingScreen.SetSubProgress(percent, GetSizeByBytes(downloadStatus.TotalBytes, percent));
|
||
await Awaiters.NextFrame;
|
||
}
|
||
LoadingScreen.HideSubProgressBar();
|
||
loadResourceDatas.Remove(assetName[0]);
|
||
if (downloadOp.Status == AsyncOperationStatus.Failed
|
||
|| downloadOp.OperationException != null)
|
||
{
|
||
result = false;
|
||
}
|
||
DateTime endTime = DateTime.Now;
|
||
Debug.Log($"Preload finished, result:{result}, time cost:{(endTime - startTime).TotalSeconds}s");
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("Preload_finish"))
|
||
{
|
||
e.AddContent("status", result ? "Succeeded" : "Failed")
|
||
.AddContent("duration_ms", (endTime - startTime).TotalMilliseconds);
|
||
if (downloadOp.OperationException != null)
|
||
{
|
||
e.AddContent("error", downloadOp.OperationException.Message);
|
||
}
|
||
}
|
||
#endif
|
||
Addressables.Release(downloadOp);
|
||
}
|
||
progress?.Invoke(1);
|
||
return result;
|
||
}
|
||
|
||
|
||
private const float byte2mb = 1024 * 1024;
|
||
private string GetSizeByBytes(long total, float percent)
|
||
{
|
||
return $"{(percent * total / byte2mb).ToString("0.0")}MB/{(total / byte2mb).ToString("0.0")}MB";
|
||
}
|
||
|
||
private string GetSizeByBytes(long total)
|
||
{
|
||
return $"{(total / byte2mb).ToString("0.00")}MB";
|
||
}
|
||
/// <summary>
|
||
/// 多线程下载资源 下载界面调用
|
||
/// </summary>
|
||
public async Task<bool> Load(string label)
|
||
{
|
||
try
|
||
{
|
||
oldName = label;
|
||
if (loadResourceDatas.ContainsKey(label))
|
||
{
|
||
curName = label;
|
||
return false;
|
||
}
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(label);
|
||
GameCore.UIManager.ShowWaitingBlock("LoadResourceService::Load");
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Load");
|
||
if (totalDownloadSize > 1)
|
||
{
|
||
Load(label, totalDownloadSize, locations.Select(_ => _.PrimaryKey).ToList());
|
||
return false;
|
||
}
|
||
Addressables.Release(locationsOp);
|
||
return true;
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Load");
|
||
Debug.LogError(e);
|
||
}
|
||
return false;
|
||
}
|
||
/// <summary>
|
||
/// 多线程下载资源 下载界面调用
|
||
/// </summary>
|
||
/// <param name="name"></param>
|
||
/// <param name="totalDownloadSize"></param>
|
||
/// <param name="keys"></param>
|
||
public async Task<bool> Loads(List<string> labels)
|
||
{
|
||
try
|
||
{
|
||
oldName = labels[0];
|
||
if (loadResourceDatas.ContainsKey(labels[0]))
|
||
{
|
||
curName = labels[0];
|
||
return false;
|
||
}
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(labels, Addressables.MergeMode.Union);
|
||
GameCore.UIManager.ShowWaitingBlock("LoadResourceService::Loads");
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Loads");
|
||
if (totalDownloadSize > 1)
|
||
{
|
||
Load(labels[0], totalDownloadSize, locations.Select(_ => _.PrimaryKey).ToList());
|
||
return false;
|
||
}
|
||
Addressables.Release(locationsOp);
|
||
return true;
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Loads");
|
||
Debug.LogError(e);
|
||
}
|
||
return false;
|
||
}
|
||
/// <summary>
|
||
/// 多线程下载资源 下载界面调用
|
||
/// </summary>
|
||
/// <param name="name"></param>
|
||
/// <param name="totalDownloadSize"></param>
|
||
/// <param name="keys"></param>
|
||
async void Load(string name, float totalDownloadSize, IList<string> keys)
|
||
{
|
||
try
|
||
{
|
||
curName = name;
|
||
AsyncOperationHandle downloadOp = Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union);
|
||
loadResourceDatas[name] = downloadOp;
|
||
LoadProgressEvent loadProgressEvent = new LoadProgressEvent() { name = name, progress = downloadOp.GetDownloadStatus().Percent, totalDownloadSize = totalDownloadSize };
|
||
while (!downloadOp.IsDone && downloadOp.Status != AsyncOperationStatus.Failed)
|
||
{
|
||
if (name == curName)
|
||
{
|
||
loadProgressEvent.progress = downloadOp.GetDownloadStatus().Percent;
|
||
loadProgressEvent.totalDownloadSize = totalDownloadSize;
|
||
GContext.Publish(loadProgressEvent);
|
||
}
|
||
await Awaiters.Seconds(0.2f);
|
||
}
|
||
|
||
if (name == curName)
|
||
{
|
||
if (downloadOp.Status == AsyncOperationStatus.Succeeded)
|
||
{
|
||
loadProgressEvent.progress = 1.0001f;
|
||
loadProgressEvent.isSucceeded = true;
|
||
}
|
||
else
|
||
{
|
||
loadProgressEvent.isFailed = true;
|
||
}
|
||
loadProgressEvent.totalDownloadSize = totalDownloadSize;
|
||
GContext.Publish(loadProgressEvent);
|
||
}
|
||
try
|
||
{
|
||
OnLoadGameEvent(downloadOp, string.Join(',', keys));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
|
||
Addressables.Release(downloadOp);
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
loadResourceDatas.Remove(name);
|
||
}
|
||
/// <summary>
|
||
/// 检查某一资源是否需要下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> CheckResource(string label)
|
||
{
|
||
try
|
||
{
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(label);
|
||
GameCore.UIManager.ShowWaitingBlock("LoadResourceService::Load");
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Load");
|
||
Addressables.Release(locationsOp);
|
||
if (totalDownloadSize > 1)
|
||
{
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
GameCore.UIManager.HideWaitingBlock("LoadResourceService::Load");
|
||
Debug.LogError(e);
|
||
}
|
||
return false;
|
||
}
|
||
/// <summary>
|
||
/// 后台静默资源下载排队,一传一大坨,然后一个包一个包排队下载
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
public async void EnqueueBundleSilently(List<string> label)
|
||
{
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(label, Addressables.MergeMode.Union);
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
if (totalDownloadSize > 0)
|
||
{
|
||
for (int i = 0; locations.Count > i; i++)
|
||
{
|
||
//判断 loadQueue 是否包含当前资源
|
||
if (loadQueue.Contains(locations[i].PrimaryKey))
|
||
{
|
||
continue;
|
||
}
|
||
loadQueue.AddLast(locations[i].PrimaryKey);
|
||
if (loadQueue.Count == 1)
|
||
{
|
||
LoadKey();
|
||
}
|
||
}
|
||
}
|
||
Addressables.Release(locationsOp);
|
||
}
|
||
|
||
// 排队下载资源,后台静默下载
|
||
async void LoadKey()
|
||
{
|
||
while (loadQueue.Count > 0)
|
||
{
|
||
string key = loadQueue.First.Value;
|
||
AsyncOperationHandle downloadOp = Addressables.DownloadDependenciesAsync(key);
|
||
await downloadOp.Task;
|
||
|
||
OnLoadGameEvent(downloadOp, key);
|
||
|
||
Addressables.Release(downloadOp);
|
||
await Awaiters.NextFrame;
|
||
loadQueue.RemoveFirst();
|
||
}
|
||
}
|
||
|
||
System.Threading.SemaphoreSlim _asyncLock { set; get; }
|
||
|
||
/// <summary>
|
||
/// 检查资源下载排队,一传一组,分组下载,如果需要下载则排队下载等待 LoadEventResourceEvent 事件,否则直接返回true
|
||
/// </summary>
|
||
/// <param name="label"></param>
|
||
/// <returns></returns>
|
||
public async Task<bool> CheckResourceLoadQueue(string key, List<string> label)
|
||
{
|
||
bool NoCheckResource = config.Get("NoCheckResource", "0") == "1";
|
||
if (NoCheckResource)
|
||
return true;
|
||
_asyncLock ??= new System.Threading.SemaphoreSlim(1, 1);
|
||
bool _isLockAcquired = false;
|
||
try
|
||
{
|
||
if (loadRecord.ContainsKey(key))
|
||
{
|
||
return loadRecord[key];
|
||
}
|
||
await _asyncLock.WaitAsync();
|
||
_isLockAcquired = true;
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(label, Addressables.MergeMode.Union);
|
||
var locations = await locationsOp.Task;
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
Addressables.Release(locationsOp);
|
||
if (totalDownloadSize > 1)
|
||
{
|
||
loadRecord[key] = false;
|
||
LoadEventResourceEvent loadEventData = new LoadEventResourceEvent();
|
||
loadEventData.key = key;
|
||
loadEventData.label = label;
|
||
if (loadEventData.label == null || loadEventData.label.Count == 0)
|
||
return false;
|
||
loadEventQueue.AddLast(loadEventData);
|
||
if (loadEventQueue.Count == 1)
|
||
{
|
||
LoadEventKey();
|
||
}
|
||
return false;
|
||
}
|
||
loadRecord[key] = true;
|
||
return true;
|
||
}
|
||
catch (System.Exception e)
|
||
{
|
||
loadRecord.Remove(key);
|
||
Debug.LogError(e);
|
||
}
|
||
finally
|
||
{
|
||
if (_isLockAcquired)
|
||
{
|
||
_asyncLock.Release();
|
||
_isLockAcquired = false;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
async void LoadEventKey()
|
||
{
|
||
LoadEventResourceing = true;
|
||
while (loadEventQueue.Count > 0)
|
||
{
|
||
LoadEventResourceEvent loadEventData = loadEventQueue.First.Value;
|
||
if (loadEventData.label == null || loadEventData.label.Count == 0)
|
||
{
|
||
loadEventQueue.RemoveFirst();
|
||
continue;
|
||
}
|
||
try
|
||
{
|
||
var locationsOp = Addressables.LoadResourceLocationsAsync(loadEventData.label, Addressables.MergeMode.Union);
|
||
var locations = await locationsOp.Task;
|
||
Addressables.Release(locationsOp);
|
||
|
||
// 检查locations是否有效
|
||
if (locations == null || locations.Count == 0)
|
||
{
|
||
loadEventData.isSucceeded = false;
|
||
GContext.Publish(loadEventData);
|
||
loadEventQueue.RemoveFirst();
|
||
continue;
|
||
}
|
||
var totalDownloadSize = await Addressables.GetDownloadSizeAsync(locations).Task;
|
||
if (totalDownloadSize > 0)
|
||
{
|
||
var keys = locations.Select(_ => _.PrimaryKey).ToList();
|
||
string keyStr = string.Join(",", keys);
|
||
Debug.Log($"资源名称:{keyStr}");
|
||
|
||
AsyncOperationHandle downloadOp = Addressables.DownloadDependenciesAsync(keys, Addressables.MergeMode.Union);
|
||
await downloadOp.Task;
|
||
await Awaiters.NextFrame;
|
||
OnLoadGameEvent(downloadOp, keyStr);
|
||
bool isSucceeded = downloadOp.Status == AsyncOperationStatus.Succeeded;
|
||
loadEventData.isSucceeded = isSucceeded;
|
||
GContext.Publish(loadEventData);
|
||
if (isSucceeded)
|
||
{
|
||
loadRecord[loadEventData.key] = isSucceeded;
|
||
}
|
||
else
|
||
{
|
||
loadRecord.Remove(loadEventData.key);
|
||
}
|
||
Addressables.Release(downloadOp);
|
||
}
|
||
else
|
||
{
|
||
Debug.Log($"资源大小:{0}");
|
||
await Awaiters.NextFrame;
|
||
loadEventData.isSucceeded = true;
|
||
GContext.Publish(loadEventData);
|
||
loadRecord[loadEventData.key] = true;
|
||
}
|
||
loadEventQueue.RemoveFirst();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"资源下载: {e.Message} {e.StackTrace}");
|
||
loadEventData.isSucceeded = false;
|
||
GContext.Publish(loadEventData);
|
||
loadRecord.Remove(loadEventData.key);
|
||
loadEventQueue.RemoveFirst();
|
||
Debug.Log($"错误下载活动资源队列 count == {loadEventQueue.Count}");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void OnLoadGameEvent(AsyncOperationHandle downloadOp, string key)
|
||
{
|
||
if (downloadOp.Status == AsyncOperationStatus.Failed || downloadOp.OperationException != null)
|
||
{
|
||
Exception exception = downloadOp.OperationException;
|
||
using (var e = GEvent.GameEvent("resource_load_finish"))
|
||
{
|
||
e.AddContent("status", "Failed")
|
||
.AddContent("keys", key);
|
||
if (exception != null)
|
||
{
|
||
e.AddContent("error", exception.Message);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|