using XGame.Editor.Asset;
using XGame.Framework.Asset.Addressable.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using XGame.Framework.i18n;
namespace XGame.Editor.Build.AssetBundles
{
///
/// assbundle的上下文
///
public class BundleContext
{
public readonly string ResStaticDir = $"/{PathDefine.ResStaticName}/";
public readonly string UiPrefabDir = "/UI/Prefabs/";
public readonly string EditorDir = "/Editor/";
public readonly string ResourcesDir = "/Resources/";
public readonly string PrefabExt = ".prefab";
public readonly string ShaderExt = ".shader";
///
/// key:guid
/// value:AssetInfo
///
private Dictionary _assetInfoMap;
///
/// key:assetPath
/// value:guid
///
private Dictionary _assetPathToGuidMap;
public Dictionary bundleDataMap;
///
/// key:bundleId
/// value:dependencies
///
private Dictionary _bundleDependenciesMap;
public HashSet bundleAssetPaths;
public Dictionary dependenciesMap;
#region Addressable资源
public string[] assetsRoots;
///
/// 等待处理的资源
///
public HashSet waittingAssetPaths;
#endregion
///
/// 贴图资源没有依赖,缓存一份贴图路径数据节省一些判断
///
public HashSet allTexturePaths;
public AssetBundleNameMode bundleNameMode;
///
/// 是否合并shader
///
public bool isMergeShader;
///
/// ab分组名字
/// key:assetPath
/// value:groupName
///
public Dictionary groupNameMap;
///
/// 排序好的AssetBundle数据
///
private List _bundleDataLst;
///
/// 排序好的AssetBundle数据
///
public List BundleDataLst => _bundleDataLst;
public BundleContext(Dictionary assetInfoMap)
{
_assetInfoMap = assetInfoMap;
bundleDataMap = new Dictionary();
_bundleDependenciesMap = new Dictionary();
bundleAssetPaths = new HashSet();
dependenciesMap = new Dictionary();
waittingAssetPaths = new HashSet();
allTexturePaths = new HashSet();
groupNameMap = new Dictionary();
Init();
}
private void Init()
{
_assetPathToGuidMap = new Dictionary();
foreach (var guid in _assetInfoMap.Keys)
{
var assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(assetPath))
{
_assetPathToGuidMap.Add(assetPath, guid);
}
}
}
#region addressable
public bool IsAddressableAsset(string guid)
{
return _assetInfoMap.ContainsKey(guid);
}
public bool TryGetAddressableNameByGuid(string guid, out string addressableName)
{
if (_assetInfoMap.TryGetValue(guid, out var info))
{
addressableName = info.addressableName;
return true;
}
addressableName = string.Empty;
return false;
}
public bool TryGetAddressableNameByPath(string assetPath, out string addressableName)
{
if (_assetPathToGuidMap.TryGetValue(assetPath, out var guid))
{
addressableName = _assetInfoMap[guid].addressableName;
return true;
}
addressableName = string.Empty;
return false;
}
///
/// key:assetPath
/// value:addressableName
///
///
public Dictionary GetAddressableInfoMap()
{
var map = new Dictionary();
foreach(var item in _assetPathToGuidMap)
{
var assetInfo = _assetInfoMap[item.Value];
if (!string.IsNullOrEmpty(assetInfo.relativePath))
{
//忽略Resources的资源
continue;
}
map.Add(item.Key, assetInfo.addressableName);
}
return map;
}
///
/// 资源路径是否有效
///
///
///
public bool IsValidPath(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return false;
}
foreach (var root in assetsRoots)
{
if (assetPath.StartsWith(root))
{
return true;
}
}
return false;
}
#endregion
///
/// 支持addressable加载
/// 多语言的单独分文件夹
/// 根据分组配置划分文件夹
/// originGuid 用于生成bundleId,一般使用guid
/// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
///
///
///
public void AddAddressableBundle(string[] assetPaths, string originGuid)
{
string groupName;
var firstAsset = assetPaths[0];
var langFlag = AddressableHelper.GetLanguageTypeByAssetPath(firstAsset);
if (langFlag == LanguageType.NONE)
{
// 取Group配置
groupNameMap.TryGetValue(firstAsset, out groupName);
}
else
{
groupName = langFlag.ToString().ToLower();
}
var originName = bundleNameMode == AssetBundleNameMode.Guid ? originGuid : AssetDatabase.GUIDToAssetPath(originGuid);
var bundleId = Crc32.GetCrc32(originName);
var count = assetPaths.Length;
var addressableNames = new string[count];
var isTextureBundle = true;
for (var i = 0; i < count; i++)
{
var assetPath = assetPaths[i];
if (isTextureBundle && allTexturePaths.Contains(assetPath) == false)
{ // 有一个资源非贴图
isTextureBundle = false;
}
if (TryGetAddressableNameByPath(assetPath, out var addressableName) == false)
{
Debug.LogWarning($"Asset can't find addressableName. Path:{assetPath}");
addressableName = assetPath;
}
addressableNames[i] = addressableName;
// 移除等待
waittingAssetPaths.Remove(assetPath);
}
var bundle = new AssetBundleData()
{
assetBundleName = string.IsNullOrEmpty(groupName) ? bundleId.ToString() : $"{groupName}/{bundleId}",
assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
assetNames = assetPaths,
addressableNames = addressableNames,
bundleId = bundleId,
originBundleName = originName,
isTextureBundle = isTextureBundle,
bundleType = AssetBundleType.Addressable
};
TryAddAssetBundle(ref bundle);
}
///
/// 普通的ab包,不支持addressable加载
/// 全部生成在根目录下,不支持分组
/// originName 用于生成bundleId,一般使用guid
/// 默认为资源所属文件夹、图集为*.spriteatlas、单资源打包则为资源自己的guid
///
///
///
///
///
public AssetBundleData AddRawAssetBundle(string[] assetPaths, string originName, bool isDependency)
{
if (bundleNameMode == AssetBundleNameMode.AssetPath && isDependency == false)
{
var path = AssetDatabase.GUIDToAssetPath(originName);
if (string.IsNullOrEmpty(path))
{
Log.Error($"AddRawAssetBundle originName is not guid. originName:{originName}");
}
else
{
originName = path;
}
}
var bundleId = Crc32.GetCrc32(originName);
var bundle = new AssetBundleData()
{
assetBundleName = bundleId.ToString(),
assetBundleVariant = Framework.Asset.Define.BUNDLE_VARIANT,
assetNames = assetPaths,
addressableNames = assetPaths,
bundleId = bundleId,
originBundleName = originName,
isTextureBundle = IsTexturePaths(assetPaths),
bundleType = isDependency ? AssetBundleType.Dependency : AssetBundleType.Raw
};
TryAddAssetBundle(ref bundle);
return bundle;
}
private bool IsTexturePaths(string[] assetPaths)
{
foreach(var assetPath in assetPaths)
{
if (allTexturePaths.Contains(assetPath) == false)
{
return false;
}
}
return true;
}
private void TryAddAssetBundle(ref AssetBundleData bundle)
{
var bundleId = bundle.bundleId;
bundleAssetPaths.UnionWith(bundle.assetNames);
if (bundleDataMap.ContainsKey(bundleId))
{
var lastName = bundleDataMap[bundleId].originBundleName;
var nextName = bundle.originBundleName;
Debug.LogError($"[XBuild] AssetBundleId重复. Id:{bundleId} LastName:{lastName} LastPath:{AssetDatabase.GUIDToAssetPath(lastName)} NextName:{nextName} NextPath:{AssetDatabase.GUIDToAssetPath(nextName)}");
return;
}
bundleDataMap.Add(bundleId, bundle);
}
private List tempLst = new List();
public string[] ToArrayBySort(HashSet assetPaths)
{
if (assetPaths.Count > 1)
{
tempLst.AddRange(assetPaths);
tempLst.Sort((a, b) => string.Compare(a, b, StringComparison.OrdinalIgnoreCase));
var array = tempLst.ToArray();
tempLst.Clear();
return array;
}
return assetPaths.ToArray();
}
///
/// 将所有AssetBundleData转成List并排序
///
public List SortAssetBundleDatas()
{
_bundleDataLst = bundleDataMap.Values.ToList();
_bundleDataLst.Sort(CompareAssetBundle);
return _bundleDataLst;
}
public int CompareAssetBundle(AssetBundleData bundleA, AssetBundleData bundleB)
{
if (bundleNameMode == AssetBundleNameMode.AssetPath)
{
var guidA = AssetDatabase.AssetPathToGUID(bundleA.originBundleName);
var guidB = AssetDatabase.AssetPathToGUID(bundleB.originBundleName);
//有assetPath的bundle排前面
var idx_0 = string.IsNullOrEmpty(guidA) == false ? -1 : 1;
var idx_1 = string.IsNullOrEmpty(guidB) == false ? -1 : 1;
var remain = idx_0 - idx_1;
if (remain != 0)
{
return remain;
}
}
else
{
var pathA = AssetDatabase.GUIDToAssetPath(bundleA.originBundleName);
var pathB = AssetDatabase.GUIDToAssetPath(bundleB.originBundleName);
//有assetPath的bundle排前面
var idx_0 = string.IsNullOrEmpty(pathA) == false ? -1 : 1;
var idx_1 = string.IsNullOrEmpty(pathB) == false ? -1 : 1;
var remain = idx_0 - idx_1;
if (remain != 0)
{
return remain;
}
if (idx_0 == -1)
{
return string.Compare(pathA, pathB, StringComparison.OrdinalIgnoreCase);
}
}
return string.Compare(bundleA.originBundleName, bundleB.originBundleName, StringComparison.OrdinalIgnoreCase);
}
public System.Diagnostics.Stopwatch bundleDependenciesSw = new System.Diagnostics.Stopwatch();
public string[] GetDependencies(uint bundleId, string[] assetNames)
{
if (_bundleDependenciesMap.TryGetValue(bundleId, out var dependencies) == false)
{
bundleDependenciesSw.Start();
dependencies = AssetDatabase.GetDependencies(assetNames, true);
bundleDependenciesSw.Stop();
_bundleDependenciesMap.Add(bundleId, dependencies);
}
return dependencies;
}
}
}