Files
back_cantanBuilding/Assets/Scripts/Services/LoadResourceService.cs
2026-05-26 16:15:54 +08:00

474 lines
17 KiB
C#

using asap.core;
using GameCore;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
public interface ILoadResourceService
{
Task<bool> Load(string label);
Task<bool> Loads(List<string> label);
//后台排队静默下载
void Load(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);
//检查是否需要下载资源,不下载
Task<bool> CheckResource(string label);
Task<bool> CheckResourceLoadQueue(string key, List<string> label);
Task<bool> CheckResourceLoadQueue(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";
}
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;
}
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;
}
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;
}
public async void Load(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 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);
}
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();
}
}
public async Task<bool> CheckResourceLoadQueue(List<string> label)
{
string key = string.Join(",", label);
return await ResourceLoadQueue(key, label);
}
public async Task<bool> CheckResourceLoadQueue(string key, List<string> label)
{
bool NoCheckResource = config.Get("NoCheckResource", "0") == "1";
if (NoCheckResource)
return true;
return await ResourceLoadQueue(key, label);
}
async Task<bool> ResourceLoadQueue(string key, List<string> label)
{
try
{
if (loadRecord.ContainsKey(key))
{
return loadRecord[key];
}
var locationsOp = Addressables.LoadResourceLocationsAsync(label, Addressables.MergeMode.Union);
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)
{
loadRecord[key] = false;
LoadEventResourceEvent loadEventData = new LoadEventResourceEvent();
loadEventData.key = key;
loadEventData.label = label;
LoadEventResource(loadEventData);
return false;
}
loadRecord[key] = true;
return true;
}
catch (System.Exception e)
{
loadRecord.Remove(key);
GameCore.UIManager.HideWaitingBlock("LoadResourceService:Load");
Debug.LogError(e);
}
return false;
}
/// <summary>
/// 排队下载活动资源
/// </summary>
/// <param name="loadEventData"></param>
void LoadEventResource(LoadEventResourceEvent loadEventData)
{
if (loadEventData.label == null || loadEventData.label.Count == 0)
return;
loadEventQueue.AddLast(loadEventData);
if (loadEventQueue.Count == 1)
{
LoadEventKey();
}
}
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)
{
Debug.Log("未找到任何资源位置");
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}");
Debug.Log($"资源大小:{GetSizeByBytes(totalDownloadSize)}");
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}");
}
}
}
public static 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);
}
}
}
}
}