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
{
///
/// 需要显示下载进度的并发下载
///
///
///
Task Load(string label);
///
/// 需要显示下载进度的并发下载
///
///
///
Task Loads(List label);
///
/// 不需要知道任何下载情况的后台静默排队下载,资源包队列下载
///
///
void EnqueueBundleSilently(List label);
///
/// 登陆游戏预下载地图资源
///
///
///
///
Task Loading(List assetName, System.Action progress);
///
/// 检查资源是否需要下载
///
///
///
Task CheckResource(string label);
///
/// 后台排队下载,如果需要下载则排队下载等待 LoadEventResourceEvent 事件,否则直接返回true,传入 List 为组队列下载
///
///
///
///
Task CheckResourceLoadQueue(string key, List label);
string curName { get; }
string oldName { get; }
}
public class LoadResourceData
{
public string name;
public AsyncOperationHandle downloadOp;
}
public struct LoadEventResourceEvent
{
public string key;
public List 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 loadResourceDatas = new Dictionary();
LinkedList loadQueue = new LinkedList();
LinkedList loadEventQueue = new LinkedList();
bool LoadEventResourceing = false;
Dictionary loadRecord = new Dictionary();
//检查资源是否需要更新
public string curName { get; private set; }
public string oldName { get; private set; }
///
/// 登陆游戏预下载地图资源
///
///
/// 进度
///
public async Task Loading(List assetName, System.Action 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";
}
///
/// 多线程下载资源 下载界面调用
///
public async Task 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;
}
///
/// 多线程下载资源 下载界面调用
///
///
///
///
public async Task Loads(List 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;
}
///
/// 多线程下载资源 下载界面调用
///
///
///
///
async void Load(string name, float totalDownloadSize, IList 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);
}
///
/// 检查某一资源是否需要下载
///
///
///
public async Task 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;
}
///
/// 后台静默资源下载排队,一传一大坨,然后一个包一个包排队下载
///
///
public async void EnqueueBundleSilently(List 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; }
///
/// 检查资源下载排队,一传一组,分组下载,如果需要下载则排队下载等待 LoadEventResourceEvent 事件,否则直接返回true
///
///
///
public async Task CheckResourceLoadQueue(string key, List 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);
}
}
}
}
}