using XGame.Framework.i18n;
using XGame.Framework.Asset.Addressable;
using XGame.Framework.Asset.Addressable.Data;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Text;
namespace XGame.Editor.Asset
{
public class AddressableBuildTask
{
///
/// 方便其它模块使用
///
public static void GenManifest()
{
var task = new AddressableBuildTask();
task.Run();
}
private IDictionary manifests;
private AddressableInfoManifest backupManifest;
private IDictionary addressableNameMap;
private IDictionary addressableIdMap;
private IDictionary guidMap;
private AddressableClassify[] queryOrder;
public AddressableBuildTask()
{
manifests = new Dictionary();
addressableNameMap = new Dictionary();
addressableIdMap = new Dictionary();
guidMap = new Dictionary();
queryOrder = new AddressableClassify[] { AddressableClassify.Product, AddressableClassify.BuiltIn, AddressableClassify.Tests };
}
private void Init()
{
LoadManifest();
DisposeInvalidAssets();
CollectAddressableAssets();
}
public void Run()
{
Init();
Collect();
Save();
GenCustomSearch();
}
private void Collect()
{
AddressableClassify[] classifys = { AddressableClassify.BuiltIn, AddressableClassify.Product, AddressableClassify.Tests };
for (int i = 0; i < classifys.Length; i++)
{
var classify = classifys[i];
string[] assetsRoots = AddressableHelper.GetAssetsRoots(classify);
foreach (var assetsRoot in assetsRoots)
{
var i18nName = string.Empty;
var langFlag = LanguageType.NONE;
if (classify == AddressableClassify.Product)
{
langFlag = AddressableHelper.GetLanguageTypeByAssetPath(assetsRoot);
if (langFlag != LanguageType.NONE)
{
i18nName = langFlag.ToString().ToLower();
}
}
FileUtil.FindFiles(assetsRoot, files =>
{
foreach (var file in files)
{
var path = FileUtil.ToRelativePath(file);// file.Substring(file.IndexOf("Assets/", StringComparison.Ordinal));
var guid = AssetDatabase.AssetPathToGUID(path);
if (ContainsGUID(guid))
{
//Debug.Log($"GUID Repeat. Path:{path}");
if (langFlag != LanguageType.NONE || AddressableHelper.IsLodAssetPath(path))
{ //多语言或者LOD资源
RefreshAssetInfoByGUID(guid, classify);
}
continue;
}
string fileName;
//取备份信息
if (TryGetBackupAssetInfoByGUID(guid, out var backupInfo))
{
fileName = backupInfo.addressableName;
}
else
{
fileName = AddressableHelper.GetFileName(path);
if (langFlag != LanguageType.NONE)
{
fileName = $"{i18nName}_{fileName}";
}
}
if (ContainsName(fileName))
{
//二次校验
TryGetAssetInfoByName(fileName, out var lastAssetInfo);
var lastPath = lastAssetInfo.GetAssetPath();
string temp = string.Empty;
if (string.IsNullOrEmpty(lastPath))
{
Debug.LogError($"Two assets have the same name. But one of the files is missing. Last {lastAssetInfo}. Next Path:{path}");
}
else if (path.Equals(lastPath))
{
Debug.LogError($"Two assets have the same path. But the GUID is different.Last {lastAssetInfo}. Next Path:{path}");
}
else
{
var ext = Path.GetExtension(path);
if (!string.IsNullOrEmpty(ext) && !ext.Equals(Path.GetExtension(lastPath)))
{
//后缀不一样
temp = ($"{fileName}{ext.Replace('.', '_')}").ToLower();
//if (langFlag != LangFlag.NONE)
//{
// temp = $"{i18nName}_{temp}";
//}
if (ContainsName(temp))
{
temp = string.Empty;
}
}
}
if (string.IsNullOrEmpty(temp))
{
temp = ($"{fileName}_{guid.Substring(0, 8)}").ToLower();
//if (langFlag != LangFlag.NONE)
//{
// temp = $"{i18nName}_{temp}";
//}
if (ContainsName(temp))
{
temp = ($"{fileName}_{guid}").ToLower();
//if (langFlag != LangFlag.NONE)
//{
// temp = $"{i18nName}_{temp}";
//}
}
}
fileName = temp;
}
//addressableId使用guid+path,预防相同路径的情况
var isInResources = AddressableHelper.IsInResources(path, out var relativePath);
//var assetType = isInResources ? AddressableAssetType.Resources : AddressableAssetType.Default;
var assetInfo = new AssetInfo()
{
addressableId = Crc32.GetCrc32($"{guid}+{path}"),
addressableName = fileName,
assetGUID = guid,
//assetType = assetType,
relativePath = relativePath,
};
if (AddAssetInfo(assetInfo))
{
Debug.Log($"guid:{guid} name:{fileName} path:{path}");
}
}
});
}
}
}
private void Save()
{
for (int i = 0; i < queryOrder.Length; i++)
{
if (manifests.TryGetValue(queryOrder[i], out var manifest))
{
manifest.Save();
}
}
backupManifest.Save();
}
public bool TryGetAssetInfoByName(string addressableName, out AssetInfo assetInfo)
{
if (addressableNameMap.TryGetValue(addressableName, out var classify))
{
if (manifests[classify].TryGetAssetInfoByName(addressableName, out var tempAsset))
{
assetInfo = tempAsset;
return true;
}
}
assetInfo = default;
return false;
}
public bool TryGetBackupAssetInfoByGUID(string guid, out AssetInfo assetInfo)
{
if (backupManifest.TryGetAssetInfoByGUID(guid, out var tempAsset))
{
assetInfo = tempAsset;
return true;
}
assetInfo = default;
return false;
}
private bool ContainsName(string addressableName)
{
if (addressableNameMap.ContainsKey(addressableName))
{
return true;
}
//for (int i = 0; i < queryOrder.Length; i++)
//{
// if (manifests.TryGetValue(queryOrder[i], out var manifest))
// {
// if (manifest.ContainsName(addressableName))
// {
// return true;
// }
// }
//}
return false;
}
private bool ContainsGUID(string guid)
{
if (guidMap.ContainsKey(guid))
{
return true;
}
//for (int i = 0; i < queryOrder.Length; i++)
//{
// if (manifests.TryGetValue(queryOrder[i], out var manifest))
// {
// if (manifest.ContainsGUID(guid))
// {
// return true;
// }
// }
//}
return false;
}
private void RefreshAssetInfoByGUID(string guid, AddressableClassify classify)
{
if (manifests.TryGetValue(classify, out var manifest))
{
manifest.RefreshAssetInfoByGUID(guid);
}
}
private bool AddAssetInfo(AssetInfo assetInfo)
{
var assetPath = assetInfo.GetAssetPath();
if (AddressableHelper.TryAssetPathToClassify(assetPath, out var classify))
{
if (manifests.TryGetValue(classify, out var manifest))
{
if (manifest.AddAssetInfo(assetInfo))
{
AddAddressableName(assetInfo.addressableName, classify);
AddAddressableId(assetInfo.addressableId, classify);
AddAssetGUID(assetInfo.assetGUID, classify);
return true;
}
}
}
return false;
}
private void LoadManifest()
{
for (int i = 0; i < queryOrder.Length; i++)
{
var classify = queryOrder[i];
var manifest = CreateManifest(classify);
if (manifest != null)
manifests.Add(classify, manifest);
}
backupManifest = CreateManifest(AddressableClassify.Backup);
}
private AddressableInfoManifest CreateManifest(AddressableClassify classify)
{
var manifest = AddressableHelper.LoadOrCreateManifest(classify);
if (manifest != null)
{
var adapter = new AddressableInfoManifest(manifest, classify);
return adapter;
}
return null;
}
private void DisposeInvalidAssets()
{
for (int i = 0; i < queryOrder.Length; i++)
{
if (manifests.TryGetValue(queryOrder[i], out var manifest))
{
manifest.DisposeInvalidAssets(AddAssetInfo);
}
}
}
private void CollectAddressableAssets()
{
addressableNameMap.Clear();
addressableIdMap.Clear();
guidMap.Clear();
for (int i = 0; i < queryOrder.Length; i++)
{
if (manifests.TryGetValue(queryOrder[i], out var manifest))
{
manifest.CollectAddressableAssets(ref addressableNameMap, ref addressableIdMap, ref guidMap);
}
}
}
private void AddAddressableName(string addressableName, AddressableClassify classify)
{
if (addressableNameMap.ContainsKey(addressableName))
{
addressableNameMap[addressableName] = classify;
}
else
{
addressableNameMap.Add(addressableName, classify);
}
}
private void AddAssetGUID(string guid, AddressableClassify classify)
{
if (guidMap.ContainsKey(guid))
{
guidMap[guid] = classify;
}
else
{
guidMap.Add(guid, classify);
}
}
private void AddAddressableId(long id, AddressableClassify classify)
{
if (addressableIdMap.ContainsKey(id))
{
addressableIdMap[id] = classify;
}
else
{
addressableIdMap.Add(id, classify);
}
}
#region 资源搜索和引用标签 https://docs.unity.cn/cn/tuanjiemanual/1.2/Manual/AssetsReferences.html
///
/// 生成CustomSearch.txt
///
private void GenCustomSearch()
{
var manifest = AddressableHelper.LoadAssetManifest(AddressableClassify.Product);
var assetInfos = manifest.assetInfos;
var assetPaths = new List();
for(var i = 0; i < assetInfos.Length; i++)
{
var assetPath = assetInfos[i].GetAssetPath();
if (string.IsNullOrEmpty(assetPath))
{
continue;
}
assetPaths.Add(assetPath);
}
var sb = new StringBuilder();
sb.AppendLine((assetPaths.Count + 4).ToString());
for(var i = 0;i < assetPaths.Count; i++)
{
sb.AppendLine(assetPaths[i]);
}
sb.AppendLine(PathDefine.BundleManifestPath);
sb.AppendLine(PathDefine.SceneAssetBundleManifestPath);
sb.AppendLine(PathDefine.ReferenceManifestPath);
sb.AppendLine(PathDefine.ProductAssetManifestPath);
var filePath = PathDefine.AddressableDataRelative + "CustomSearch.txt";
File.WriteAllText(filePath, sb.ToString());
AssetDatabase.Refresh();
}
#endregion
}
}