VersionFlow/Assets/VersionFlow/Runtime/BundleManager.cs
2025-08-08 10:33:30 +08:00

484 lines
16 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace VersionFlow.Runtime
{
public class BundleManager
{
/// <summary> Bundle名:ABEntity </summary>
private readonly Dictionary<string, ABEntity> m_entities = new Dictionary<string, ABEntity>();
/// <summary> 资源路径:ABEntity </summary>
private readonly Dictionary<string, ABEntity> m_assetPathToAB = new Dictionary<string, ABEntity>();
/// <summary> 组名:ABEntity </summary>
private readonly Dictionary<string, List<ABEntity>> m_groupToAB = new Dictionary<string, List<ABEntity>>();
/// <summary> Shader专属Bundle </summary>
private readonly List<ABEntity> m_shaderBundles = new List<ABEntity>();
/// <summary> 全部已加载shader </summary>
private readonly Dictionary<string, Shader> m_shaders = new Dictionary<string, Shader>();
/// <summary> shader变体收集器 </summary>
public ShaderVariantCollection SVC { get; private set; }
private readonly HashSet<LoadingChain> m_loadingChains = new HashSet<LoadingChain>();
private readonly Type m_gameObjectType = typeof(GameObject);
/// <summary> 可选Bundle组:ABEntity </summary>
private readonly Dictionary<string, OptionalBundleDownloader> m_optionGroupDownloader = new Dictionary<string, OptionalBundleDownloader>();
public string Version { get; set; } = "N/A";
internal HashSet<LoadingChain> GetLoadingChains() => m_loadingChains;
internal BundleManager(BundleManifest local, BundleManifest remote)
{
//初始化BundleManager
Stopwatch sw = Stopwatch.StartNew();
//创建Bundle对象和资源清单
foreach (var remoteBundle in remote.Bundles)
{
var localBundle = local.GetBundleByName(remoteBundle.BundleName);
var createBundle = new ABEntity(remoteBundle, localBundle != null && localBundle.Hash == remoteBundle.Hash);
m_entities[remoteBundle.BundleName] = createBundle;
//建立AssetPath到AB的缓存
foreach (var path in createBundle.Assets)
{
m_assetPathToAB[path] = createBundle;
}
//建立Group到AB的缓存
if (remoteBundle.Group != null)
{
if (!m_groupToAB.ContainsKey(remoteBundle.Group)) m_groupToAB[remoteBundle.Group] = new List<ABEntity>();
m_groupToAB[remoteBundle.Group].Add(createBundle);
if (remoteBundle.OptionBundle)
{
if (!m_optionGroupDownloader.ContainsKey(remoteBundle.Group)) m_optionGroupDownloader[remoteBundle.Group] = new OptionalBundleDownloader(remoteBundle.Group);
m_optionGroupDownloader[remoteBundle.Group].CollectAB(createBundle);
}
}
//记录shader专用bundle
if (createBundle.m_isShaderBundle)
m_shaderBundles.Add(createBundle);
}
//创建引用关系和资源清单
foreach (var remoteBundle in remote.Bundles)
{
var abEntity = m_entities[remoteBundle.BundleName];
foreach (var bundleName in remoteBundle.Dependencies)
{
var depAB = m_entities[bundleName];
abEntity.m_dependencyAB.Add(depAB);
}
}
//计算可选组下载状态
foreach (var optionDownloader in m_optionGroupDownloader.Values)
{
optionDownloader.CalcState();
}
sw.Stop();
VersionFlowX.Log($"创建资源清单耗时:{sw.Elapsed.Milliseconds}ms");
}
internal BundleManager() { }
internal ABEntity GetEntityByAssetPath(string assetPath)
{
m_assetPathToAB.TryGetValue(assetPath, out var bundle);
return bundle;
}
internal IEnumerator Internal_LoadAssetAsync<T>(string path) where T : UnityEngine.Object
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
yield return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
#endif
}
else
{
path = path.ToLower();
LoadingChain targetChain = GetLoadChain(path);
if (targetChain == null)
{
yield return null;
}
else
{
var itor = targetChain.LoadAssetAsync<T>(path);
while (itor.MoveNext()) yield return itor.Current;
yield return itor.Current;
}
}
}
internal ABEntity GetABEntity(string abName)
{
m_entities.TryGetValue(abName, out var aBEntity);
return aBEntity;
}
internal LoadingChain GetLoadChain(string path)
{
m_assetPathToAB.TryGetValue(path, out var ab);
if (ab == null)
{
VersionFlowX.Error($"没有任何AssetBundle包含<color=cyan>{path}</color>");
return null;
}
LoadingChain targetChain = GetLoadChain(ab, false);
return targetChain;
}
internal LoadingChain GetLoadChain(ABEntity ab, bool shaderOnly)
{
LoadingChain targetChain = null;
foreach (var chain in m_loadingChains)
{
if (chain.m_topEntity == ab)
{
targetChain = chain;
break;
}
}
if (targetChain == null)
{
targetChain = LoadingChain.Create(ab, shaderOnly);
m_loadingChains.Add(targetChain);
}
return targetChain;
}
internal void LoadShaderBundles()
{
return;
VersionFlowX.Log("开始加载所有的ShaderBundle");
UnityResourceAPIOverride overrideAPI = new UnityResourceAPIOverride();
ResourcesAPI.overrideAPI = overrideAPI;
foreach (var shaderBundle in m_shaderBundles)
{
var chain = GetLoadChain(shaderBundle, true);
chain.LoadAllBundle();
foreach (var shaderPath in shaderBundle.Assets)
{
var shaderAsset = LoadAsset<UnityEngine.Object>(shaderPath);
if (shaderAsset == null) continue;
VersionFlowX.Log($"加载Shader:<color=cyan>{shaderAsset.name}</color>");
if (shaderAsset is Shader shader)
m_shaders[shader.name] = shader;
else if (shaderAsset is ShaderVariantCollection svc)
SVC = svc;
}
}
}
internal Shader GetShader(string name)
{
m_shaders.TryGetValue(name, out var shader);
return shader;
}
public void LoadScene(string path)
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
UnityEditor.SceneManagement.EditorSceneManager.LoadSceneInPlayMode(path, new LoadSceneParameters(LoadSceneMode.Single));
#else
throw new Exception("不可能!");
#endif
}
else
{
//找到已加载场景的loadchain
LoadingChain hasSceneChain = null;
foreach (var chain in m_loadingChains)
{
if (chain.m_currentLoadScenePath != null)
{
hasSceneChain = chain;
break;
}
}
var targetChain = GetLoadChain(path);
if (targetChain == null)
{
return;
}
targetChain.LoadScene(path);
if (hasSceneChain != null) hasSceneChain.m_currentLoadScenePath = null;
}
}
public AsyncOperation LoadSceneAsync(string path)
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
return UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(path, new LoadSceneParameters(LoadSceneMode.Single));
#else
throw new Exception("不可能!");
#endif
}
else
{
//找到已加载场景的loadchain
LoadingChain hasSceneChain = null;
foreach (var chain in m_loadingChains)
{
if (chain.m_currentLoadScenePath != null)
{
hasSceneChain = chain;
break;
}
}
var targetChain = GetLoadChain(path);
if (targetChain == null)
{
return null;
}
var req = targetChain.LoadSceneAsync(path);
req.completed += (asyncop) =>
{
targetChain.m_currentLoadScenePath = path;
if (hasSceneChain != null) hasSceneChain.m_currentLoadScenePath = null;
};
return req;
}
}
public T LoadAsset<T>(string path) where T : UnityEngine.Object
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
return UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
#endif
}
if (typeof(T) == m_gameObjectType)
{
VersionFlowX.Log("GameObject类型Asset不可以使用LoadAsset的方式,请使用Instantiate的方式");
return null;
}
path = path.ToLower();
LoadingChain targetChain = GetLoadChain(path);
if (targetChain == null)
{
return null;
}
var asset = targetChain.LoadAsset<T>(path);
return asset;
}
public GameObject Instantiate(string path, Transform parent = null)
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
var go = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (go == null)
{
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
return null;
}
return GameObject.Instantiate(go, parent);
#endif
}
path = path.ToLower();
LoadingChain targetChain = GetLoadChain(path);
if (targetChain == null)
{
return null;
}
var prefabInstance = targetChain.Instantiate(path, parent);
return prefabInstance;
}
public InstantiateRequest InstantiateAsync(string path)
{
var request = new InstantiateRequest(Internal_InstantiateAsync(path));
request.Start();
return request;
}
internal IEnumerator Internal_InstantiateAsync(string path)
{
if (!VersionFlowX.Setting.Enable)
{
#if UNITY_EDITOR
var go = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (go == null)
{
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
yield break;
}
else yield return GameObject.Instantiate(go);
#endif
}
else
{
path = path.ToLower();
LoadingChain targetChain = GetLoadChain(path);
if (targetChain == null)
{
VersionFlowX.Error($"没有找到<color=cyan>{path}</color>的资源");
yield break;
}
var itor = targetChain.InstantiateAsync(path);
while (itor.MoveNext()) yield return itor.Current;
yield return itor.Current;
}
}
public LoadAssetRequest<T> LoadAssetAsync<T>(string path) where T : UnityEngine.Object
{
if (typeof(T) == m_gameObjectType)
{
VersionFlowX.Error("GameObject类型Asset不可以使用LoadAsset的方式,请使用Instantiate的方式");
return null;
}
var request = new LoadAssetRequest<T>(Internal_LoadAssetAsync<T>(path));
request.Start();
return request;
}
public void ReleaseAsset<T>(ref T asset) where T : UnityEngine.Object
{
if (!VersionFlowX.Setting.Enable)
{
asset = null;
}
else
{
foreach (var chain in m_loadingChains)
{
chain.UnLoadAsset(asset);
}
asset = null;
if (VersionFlowX.Setting.AutoReleaseBundle)
TryUnLoadBundles();
}
}
public void ReleaseAsset<T>(IList<T> assetList) where T : UnityEngine.Object
{
foreach (var item in assetList)
{
var temp = item;
ReleaseAsset(ref temp);
}
assetList.Clear();
}
HashSet<LoadingChain> m_needRemoveChains = new HashSet<LoadingChain>();
/// <summary>
/// 检查已加载的Bundle的引用计数为0并且没有任何依赖bundle被加载
/// 然后移除它
/// </summary>
public void TryUnLoadBundles()
{
if (!VersionFlowX.Setting.Enable)
{
return;
}
m_needRemoveChains.Clear();
foreach (var chain in m_loadingChains)
{
if (chain.TryDispose())
m_needRemoveChains.Add(chain);
}
m_loadingChains.ExceptWith(m_needRemoveChains);
}
/// <summary>
/// 指定GroupName,获得可选Bundle组的下载器,无论该组是否已完成下载,或是正在下载,或没有下载
/// </summary>
/// <param name="groupName">在builder中配置的Group名称,并且该Group需要设置为OptionBundle</param>
/// <returns></returns>
public OptionalBundleDownloader GetDownloaderByGroup(string groupName)
{
m_optionGroupDownloader.TryGetValue(groupName, out var downloader);
return downloader;
}
/// <summary> 获得所有设置为OptionBundle的组的下载器,无论该组是否已完成下载,或是正在下载,或没有下载 </summary>
/// <returns></returns>
public List<OptionalBundleDownloader> GetAllOptionBundleDownloader()
{
return m_optionGroupDownloader.Values.ToList();
}
}
[Serializable]
public class Bundle
{
public string BundleName;
public List<string> Assets;
public List<string> Dependencies;
public string Hash;
public long Size;
/// <summary> 所属Group </summary>
public string Group;
/// <summary> 可选资源包 </summary>
public bool OptionBundle;
/// <summary> Shader专属bundle </summary>
public bool ShaderBundle;
/// <summary> 随包资源包 </summary>
public bool InstallBundle;
internal void CopyFrom(Bundle bundle)
{
BundleName = bundle.BundleName;
Assets = bundle.Assets;
Dependencies = bundle.Dependencies;
Hash = bundle.Hash;
Size = bundle.Size;
OptionBundle = bundle.OptionBundle;
Group = bundle.Group;
ShaderBundle = bundle.ShaderBundle;
InstallBundle = bundle.InstallBundle;
}
}
}