Support addressable location

支持可寻址资源定位
pull/9/head
hevinci 2022-04-28 17:23:31 +08:00
parent 775c724840
commit 2cb006f3d9
40 changed files with 799 additions and 235 deletions

View File

@ -202,6 +202,7 @@ namespace YooAsset.Editor
buildParameters.OutputRoot = defaultOutputRoot; buildParameters.OutputRoot = defaultOutputRoot;
buildParameters.BuildTarget = _buildTarget; buildParameters.BuildTarget = _buildTarget;
buildParameters.BuildVersion = _buildVersionField.value; buildParameters.BuildVersion = _buildVersionField.value;
buildParameters.EnableAddressable = AssetBundleGrouperSettingData.Setting.EnableAddressable;
buildParameters.CompressOption = (ECompressOption)_compressionField.value; buildParameters.CompressOption = (ECompressOption)_compressionField.value;
buildParameters.AppendFileExtension = _appendExtensionToggle.value; buildParameters.AppendFileExtension = _appendExtensionToggle.value;
buildParameters.EncryptionServices = CreateEncryptionServicesInstance(); buildParameters.EncryptionServices = CreateEncryptionServicesInstance();

View File

@ -11,6 +11,11 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public string BundleName { private set; get; } public string BundleName { private set; get; }
/// <summary>
/// 可寻址地址
/// </summary>
public string Address { private set; get; }
/// <summary> /// <summary>
/// 资源路径 /// 资源路径
/// </summary> /// </summary>
@ -53,8 +58,9 @@ namespace YooAsset.Editor
public List<BuildAssetInfo> AllDependAssetInfos { private set; get; } public List<BuildAssetInfo> AllDependAssetInfos { private set; get; }
public BuildAssetInfo(string assetPath, bool isRawAsset, bool notWriteToAssetList) public BuildAssetInfo(string address, string assetPath, bool isRawAsset, bool notWriteToAssetList)
{ {
Address = address;
AssetPath = assetPath; AssetPath = assetPath;
IsRawAsset = isRawAsset; IsRawAsset = isRawAsset;
NotWriteToAssetList = notWriteToAssetList; NotWriteToAssetList = notWriteToAssetList;

View File

@ -26,7 +26,7 @@ namespace YooAsset.Editor
{ {
if (buildAssetDic.ContainsKey(collectAssetInfo.AssetPath) == false) if (buildAssetDic.ContainsKey(collectAssetInfo.AssetPath) == false)
{ {
var buildAssetInfo = new BuildAssetInfo(collectAssetInfo.AssetPath, collectAssetInfo.IsRawAsset, collectAssetInfo.NotWriteToAssetList); var buildAssetInfo = new BuildAssetInfo(collectAssetInfo.Address, collectAssetInfo.AssetPath, collectAssetInfo.IsRawAsset, collectAssetInfo.NotWriteToAssetList);
buildAssetInfo.SetBundleName(collectAssetInfo.BundleName); buildAssetInfo.SetBundleName(collectAssetInfo.BundleName);
buildAssetInfo.AddAssetTags(collectAssetInfo.AssetTags); buildAssetInfo.AddAssetTags(collectAssetInfo.AssetTags);
buildAssetDic.Add(collectAssetInfo.AssetPath, buildAssetInfo); buildAssetDic.Add(collectAssetInfo.AssetPath, buildAssetInfo);
@ -100,7 +100,7 @@ namespace YooAsset.Editor
var buildAssetInfo = pair.Value; var buildAssetInfo = pair.Value;
if (buildAssetInfo.BundleNameIsValid() == false) if (buildAssetInfo.BundleNameIsValid() == false)
{ {
string shaderBundleName = AssetBundleCollector.CollectShaderBundleName(buildAssetInfo.AssetPath); string shaderBundleName = AssetBundleGrouperSettingHelper.CollectShaderBundleName(buildAssetInfo.AssetPath);
if (string.IsNullOrEmpty(shaderBundleName) == false) if (string.IsNullOrEmpty(shaderBundleName) == false)
{ {
buildAssetInfo.SetBundleName(shaderBundleName); buildAssetInfo.SetBundleName(shaderBundleName);
@ -108,7 +108,7 @@ namespace YooAsset.Editor
else else
{ {
string bundleName = defaultPackRule.GetBundleName(new PackRuleData(buildAssetInfo.AssetPath)); string bundleName = defaultPackRule.GetBundleName(new PackRuleData(buildAssetInfo.AssetPath));
bundleName = AssetBundleCollector.CorrectBundleName(bundleName, false); bundleName = AssetBundleGrouperSettingHelper.CorrectBundleName(bundleName, false);
buildAssetInfo.SetBundleName(bundleName); buildAssetInfo.SetBundleName(bundleName);
} }
} }

View File

@ -29,6 +29,11 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public int BuildVersion; public int BuildVersion;
/// <summary>
/// 启用可寻址资源定位
/// </summary>
public bool EnableAddressable = false;
/// <summary> /// <summary>
/// 启用自动分包机制 /// 启用自动分包机制
/// 说明:自动分包机制可以实现资源零冗余 /// 说明:自动分包机制可以实现资源零冗余

View File

@ -33,6 +33,11 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public int BuildVersion; public int BuildVersion;
/// <summary>
/// 启用可寻址资源定位
/// </summary>
public bool EnableAddressable;
/// <summary> /// <summary>
/// 启用自动分包机制 /// 启用自动分包机制
/// </summary> /// </summary>

View File

@ -28,10 +28,11 @@ namespace YooAsset.Editor
// 创建新补丁清单 // 创建新补丁清单
PatchManifest patchManifest = new PatchManifest(); PatchManifest patchManifest = new PatchManifest();
patchManifest.EnableAddressable = buildParameters.Parameters.EnableAddressable;
patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion; patchManifest.ResourceVersion = buildParameters.Parameters.BuildVersion;
patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags; patchManifest.BuildinTags = buildParameters.Parameters.BuildinTags;
patchManifest.BundleList = GetAllPatchBundle(buildParameters, buildMapContext, encryptionContext); patchManifest.BundleList = GetAllPatchBundle(buildParameters, buildMapContext, encryptionContext);
patchManifest.AssetList = GetAllPatchAsset(buildMapContext, patchManifest); patchManifest.AssetList = GetAllPatchAsset(buildParameters, buildMapContext, patchManifest);
// 创建补丁清单文件 // 创建补丁清单文件
string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettingsData.GetPatchManifestFileName(resourceVersion)}"; string manifestFilePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettingsData.GetPatchManifestFileName(resourceVersion)}";
@ -126,7 +127,8 @@ namespace YooAsset.Editor
/// <summary> /// <summary>
/// 获取资源列表 /// 获取资源列表
/// </summary> /// </summary>
private List<PatchAsset> GetAllPatchAsset(BuildMapContext buildMapContext, PatchManifest patchManifest) private List<PatchAsset> GetAllPatchAsset(AssetBundleBuilder.BuildParametersContext buildParameters,
BuildMapContext buildMapContext, PatchManifest patchManifest)
{ {
List<PatchAsset> result = new List<PatchAsset>(1000); List<PatchAsset> result = new List<PatchAsset>(1000);
foreach (var bundleInfo in buildMapContext.BundleInfos) foreach (var bundleInfo in buildMapContext.BundleInfos)
@ -135,6 +137,10 @@ namespace YooAsset.Editor
foreach (var assetInfo in assetInfos) foreach (var assetInfo in assetInfos)
{ {
PatchAsset patchAsset = new PatchAsset(); PatchAsset patchAsset = new PatchAsset();
if (buildParameters.Parameters.EnableAddressable)
patchAsset.Address = assetInfo.Address;
else
patchAsset.Address = string.Empty;
patchAsset.AssetPath = assetInfo.AssetPath; patchAsset.AssetPath = assetInfo.AssetPath;
patchAsset.BundleID = GetAssetBundleID(assetInfo.BundleName, patchManifest); patchAsset.BundleID = GetAssetBundleID(assetInfo.BundleName, patchManifest);
patchAsset.DependIDs = GetAssetBundleDependIDs(patchAsset.BundleID, assetInfo, patchManifest); patchAsset.DependIDs = GetAssetBundleDependIDs(patchAsset.BundleID, assetInfo, patchManifest);

View File

@ -29,6 +29,7 @@ namespace YooAsset.Editor
buildReport.Summary.BuildSeconds = buildParameters.GetBuildingSeconds(); buildReport.Summary.BuildSeconds = buildParameters.GetBuildingSeconds();
buildReport.Summary.BuildTarget = buildParameters.Parameters.BuildTarget; buildReport.Summary.BuildTarget = buildParameters.Parameters.BuildTarget;
buildReport.Summary.BuildVersion = buildParameters.Parameters.BuildVersion; buildReport.Summary.BuildVersion = buildParameters.Parameters.BuildVersion;
buildReport.Summary.EnableAddressable = buildParameters.Parameters.EnableAddressable;
buildReport.Summary.EnableAutoCollect = buildParameters.Parameters.EnableAutoCollect; buildReport.Summary.EnableAutoCollect = buildParameters.Parameters.EnableAutoCollect;
buildReport.Summary.AppendFileExtension = buildParameters.Parameters.AppendFileExtension; buildReport.Summary.AppendFileExtension = buildParameters.Parameters.AppendFileExtension;
buildReport.Summary.AutoCollectShaders = AssetBundleGrouperSettingData.Setting.AutoCollectShaders; buildReport.Summary.AutoCollectShaders = AssetBundleGrouperSettingData.Setting.AutoCollectShaders;

View File

@ -30,7 +30,7 @@ namespace YooAsset.Editor
if (isRawFile) if (isRawFile)
{ {
if (bundleInfo.BuildinAssets.Count != 1) if (bundleInfo.BuildinAssets.Count != 1)
throw new Exception("The bundle does not support multiple raw asset : {bundleInfo.BundleName}"); throw new Exception($"The bundle does not support multiple raw asset : {bundleInfo.BundleName}");
continue; continue;
} }

View File

@ -14,6 +14,11 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public string CollectPath = string.Empty; public string CollectPath = string.Empty;
/// <summary>
/// 寻址规则类名
/// </summary>
public string AddressRuleName = string.Empty;
/// <summary> /// <summary>
/// 打包规则类名 /// 打包规则类名
/// </summary> /// </summary>
@ -24,6 +29,7 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public string FilterRuleName = string.Empty; public string FilterRuleName = string.Empty;
/// <summary> /// <summary>
/// 不写入资源列表 /// 不写入资源列表
/// </summary> /// </summary>
@ -34,9 +40,22 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public string AssetTags = string.Empty; public string AssetTags = string.Empty;
[NonSerialized]
public object UserData;
/// <summary>
/// 收集器是否有效
/// </summary>
public bool IsValid()
{
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(CollectPath) == null)
return false;
if (AssetBundleGrouperSettingData.HasPackRuleName(PackRuleName) == false)
return false;
if (AssetBundleGrouperSettingData.HasFilterRuleName(FilterRuleName) == false)
return false;
if (AssetBundleGrouperSettingData.HasAddressRuleName(AddressRuleName) == false)
return false;
return true;
}
/// <summary> /// <summary>
/// 检测配置错误 /// 检测配置错误
@ -51,6 +70,9 @@ namespace YooAsset.Editor
if (AssetBundleGrouperSettingData.HasFilterRuleName(FilterRuleName) == false) if (AssetBundleGrouperSettingData.HasFilterRuleName(FilterRuleName) == false)
throw new Exception($"Invalid {nameof(IFilterRule)} class type : {FilterRuleName}"); throw new Exception($"Invalid {nameof(IFilterRule)} class type : {FilterRuleName}");
if (AssetBundleGrouperSettingData.HasAddressRuleName(AddressRuleName) == false)
throw new Exception($"Invalid {nameof(IAddressRule)} class type : {AddressRuleName}");
} }
/// <summary> /// <summary>
@ -58,6 +80,7 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public List<CollectAssetInfo> GetAllCollectAssets(AssetBundleGrouper grouper) public List<CollectAssetInfo> GetAllCollectAssets(AssetBundleGrouper grouper)
{ {
Dictionary<string, string> adressTemper = new Dictionary<string, string>(1000);
Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(1000); Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(1000);
bool isRawAsset = PackRuleName == nameof(PackRawFile); bool isRawAsset = PackRuleName == nameof(PackRawFile);
@ -74,9 +97,10 @@ namespace YooAsset.Editor
continue; continue;
if (result.ContainsKey(assetPath) == false) if (result.ContainsKey(assetPath) == false)
{ {
string address = GetAddress(grouper, assetPath);
string bundleName = GetBundleName(grouper, assetPath, isRawAsset); string bundleName = GetBundleName(grouper, assetPath, isRawAsset);
List<string> assetTags = GetAssetTags(grouper); List<string> assetTags = GetAssetTags(grouper);
var collectAssetInfo = new CollectAssetInfo(bundleName, assetPath, assetTags, isRawAsset, NotWriteToAssetList); var collectAssetInfo = new CollectAssetInfo(bundleName, address, assetPath, assetTags, isRawAsset, NotWriteToAssetList);
collectAssetInfo.DependAssets = GetAllDependencies(assetPath); collectAssetInfo.DependAssets = GetAllDependencies(assetPath);
result.Add(assetPath, collectAssetInfo); result.Add(assetPath, collectAssetInfo);
} }
@ -94,9 +118,10 @@ namespace YooAsset.Editor
if (isRawAsset && NotWriteToAssetList) if (isRawAsset && NotWriteToAssetList)
UnityEngine.Debug.LogWarning($"Are you sure raw file are not write to asset list : {assetPath}"); UnityEngine.Debug.LogWarning($"Are you sure raw file are not write to asset list : {assetPath}");
string address = GetAddress(grouper, assetPath);
string bundleName = GetBundleName(grouper, assetPath, isRawAsset); string bundleName = GetBundleName(grouper, assetPath, isRawAsset);
List<string> assetTags = GetAssetTags(grouper); List<string> assetTags = GetAssetTags(grouper);
var collectAssetInfo = new CollectAssetInfo(bundleName, assetPath, assetTags, isRawAsset, NotWriteToAssetList); var collectAssetInfo = new CollectAssetInfo(bundleName, address, assetPath, assetTags, isRawAsset, NotWriteToAssetList);
collectAssetInfo.DependAssets = GetAllDependencies(assetPath); collectAssetInfo.DependAssets = GetAllDependencies(assetPath);
result.Add(assetPath, collectAssetInfo); result.Add(assetPath, collectAssetInfo);
} }
@ -106,10 +131,28 @@ namespace YooAsset.Editor
} }
} }
// 检测可寻址地址是否重复
if (AssetBundleGrouperSettingData.Setting.EnableAddressable)
{
foreach (var collectInfo in result)
{
string address = collectInfo.Value.Address;
if (adressTemper.ContainsKey(address) == false)
{
adressTemper.Add(address, address);
}
else
{
throw new Exception($"The address is existed : {address} in collector : {CollectPath}");
}
}
}
// 返回列表 // 返回列表
return result.Values.ToList(); return result.Values.ToList();
} }
private bool IsValidateAsset(string assetPath) private bool IsValidateAsset(string assetPath)
{ {
if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false) if (assetPath.StartsWith("Assets/") == false && assetPath.StartsWith("Packages/") == false)
@ -143,9 +186,18 @@ namespace YooAsset.Editor
IFilterRule filterRuleInstance = AssetBundleGrouperSettingData.GetFilterRuleInstance(FilterRuleName); IFilterRule filterRuleInstance = AssetBundleGrouperSettingData.GetFilterRuleInstance(FilterRuleName);
return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath)); return filterRuleInstance.IsCollectAsset(new FilterRuleData(assetPath));
} }
private string GetAddress(AssetBundleGrouper grouper, string assetPath)
{
if (NotWriteToAssetList)
return assetPath;
IAddressRule addressRuleInstance = AssetBundleGrouperSettingData.GetAddressRuleInstance(AddressRuleName);
string adressValue = addressRuleInstance.GetAssetAddress(new AddressRuleData(assetPath, CollectPath, grouper.GrouperName));
return adressValue;
}
private string GetBundleName(AssetBundleGrouper grouper, string assetPath, bool isRawAsset) private string GetBundleName(AssetBundleGrouper grouper, string assetPath, bool isRawAsset)
{ {
string shaderBundleName = CollectShaderBundleName(assetPath); string shaderBundleName = AssetBundleGrouperSettingHelper.CollectShaderBundleName(assetPath);
if (string.IsNullOrEmpty(shaderBundleName) == false) if (string.IsNullOrEmpty(shaderBundleName) == false)
return shaderBundleName; return shaderBundleName;
@ -153,7 +205,7 @@ namespace YooAsset.Editor
{ {
IPackRule packRuleInstance = AssetBundleGrouperSettingData.GetPackRuleInstance(PackRuleName); IPackRule packRuleInstance = AssetBundleGrouperSettingData.GetPackRuleInstance(PackRuleName);
string bundleName = packRuleInstance.GetBundleName(new PackRuleData(assetPath, CollectPath, grouper.GrouperName)); string bundleName = packRuleInstance.GetBundleName(new PackRuleData(assetPath, CollectPath, grouper.GrouperName));
return CorrectBundleName(bundleName, isRawAsset); return AssetBundleGrouperSettingHelper.CorrectBundleName(bundleName, isRawAsset);
} }
} }
private List<string> GetAssetTags(AssetBundleGrouper grouper) private List<string> GetAssetTags(AssetBundleGrouper grouper)
@ -178,40 +230,5 @@ namespace YooAsset.Editor
} }
return result; return result;
} }
/// <summary>
/// 收集着色器的资源包名称
/// </summary>
public static string CollectShaderBundleName(string assetPath)
{
// 如果自动收集所有的着色器
if (AssetBundleGrouperSettingData.Setting.AutoCollectShaders)
{
System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
if (assetType == typeof(UnityEngine.Shader))
{
string bundleName = AssetBundleGrouperSettingData.Setting.ShadersBundleName;
return CorrectBundleName(bundleName, false);
}
}
return null;
}
/// <summary>
/// 修正资源包名称
/// </summary>
public static string CorrectBundleName(string bundleName, bool isRawBundle)
{
if (isRawBundle)
{
string fullName = $"{bundleName}.{YooAssetSettingsData.Setting.RawFileVariant}";
return EditorTools.GetRegularPath(fullName).ToLower();
}
else
{
string fullName = $"{bundleName}.{YooAssetSettingsData.Setting.AssetBundleFileVariant}";
return EditorTools.GetRegularPath(fullName).ToLower(); ;
}
}
} }
} }

View File

@ -36,7 +36,7 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public void CheckConfigError() public void CheckConfigError()
{ {
foreach(var collector in Collectors) foreach (var collector in Collectors)
{ {
collector.CheckConfigError(); collector.CheckConfigError();
} }
@ -47,13 +47,14 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public List<CollectAssetInfo> GetAllCollectAssets() public List<CollectAssetInfo> GetAllCollectAssets()
{ {
Dictionary<string, string> adressTemper = new Dictionary<string, string>(10000);
Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(10000); Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(10000);
foreach(var collector in Collectors) foreach (var collector in Collectors)
{ {
var temper = collector.GetAllCollectAssets(this); var temper = collector.GetAllCollectAssets(this);
foreach(var assetInfo in temper) foreach (var assetInfo in temper)
{ {
if(result.ContainsKey(assetInfo.AssetPath) == false) if (result.ContainsKey(assetInfo.AssetPath) == false)
{ {
result.Add(assetInfo.AssetPath, assetInfo); result.Add(assetInfo.AssetPath, assetInfo);
} }
@ -63,6 +64,24 @@ namespace YooAsset.Editor
} }
} }
} }
// 检测可寻址地址是否重复
if (AssetBundleGrouperSettingData.Setting.EnableAddressable)
{
foreach (var collectInfo in result)
{
string address = collectInfo.Value.Address;
if (adressTemper.ContainsKey(address) == false)
{
adressTemper.Add(address, address);
}
else
{
throw new Exception($"The address is existed : {address} in grouper : {GrouperName}");
}
}
}
return result.Values.ToList(); return result.Values.ToList();
} }
} }

View File

@ -18,6 +18,7 @@ namespace YooAsset.Editor
public const string XmlGrouperDesc = "GrouperDesc"; public const string XmlGrouperDesc = "GrouperDesc";
public const string XmlCollector = "Collector"; public const string XmlCollector = "Collector";
public const string XmlDirectory = "CollectPath"; public const string XmlDirectory = "CollectPath";
public const string XmlAddressRule = "AddressRule";
public const string XmlPackRule = "PackRule"; public const string XmlPackRule = "PackRule";
public const string XmlFilterRule = "FilterRule"; public const string XmlFilterRule = "FilterRule";
public const string XmlNotWriteToAssetList = "NotWriteToAssetList"; public const string XmlNotWriteToAssetList = "NotWriteToAssetList";
@ -82,6 +83,8 @@ namespace YooAsset.Editor
XmlElement collectorElement = collectorNode as XmlElement; XmlElement collectorElement = collectorNode as XmlElement;
if (collectorElement.HasAttribute(XmlDirectory) == false) if (collectorElement.HasAttribute(XmlDirectory) == false)
throw new Exception($"Not found attribute {XmlDirectory} in {XmlCollector}"); throw new Exception($"Not found attribute {XmlDirectory} in {XmlCollector}");
if (collectorElement.HasAttribute(XmlAddressRule) == false)
throw new Exception($"Not found attribute {XmlAddressRule} in {XmlCollector}");
if (collectorElement.HasAttribute(XmlPackRule) == false) if (collectorElement.HasAttribute(XmlPackRule) == false)
throw new Exception($"Not found attribute {XmlPackRule} in {XmlCollector}"); throw new Exception($"Not found attribute {XmlPackRule} in {XmlCollector}");
if (collectorElement.HasAttribute(XmlFilterRule) == false) if (collectorElement.HasAttribute(XmlFilterRule) == false)
@ -93,6 +96,7 @@ namespace YooAsset.Editor
AssetBundleCollector collector = new AssetBundleCollector(); AssetBundleCollector collector = new AssetBundleCollector();
collector.CollectPath = collectorElement.GetAttribute(XmlDirectory); collector.CollectPath = collectorElement.GetAttribute(XmlDirectory);
collector.AddressRuleName = collectorElement.GetAttribute(XmlAddressRule);
collector.PackRuleName = collectorElement.GetAttribute(XmlPackRule); collector.PackRuleName = collectorElement.GetAttribute(XmlPackRule);
collector.FilterRuleName = collectorElement.GetAttribute(XmlFilterRule); collector.FilterRuleName = collectorElement.GetAttribute(XmlFilterRule);
collector.NotWriteToAssetList = collectorElement.GetAttribute(XmlNotWriteToAssetList) == "True" ? true : false; collector.NotWriteToAssetList = collectorElement.GetAttribute(XmlNotWriteToAssetList) == "True" ? true : false;
@ -146,6 +150,7 @@ namespace YooAsset.Editor
{ {
var collectorElement = xmlDoc.CreateElement(XmlCollector); var collectorElement = xmlDoc.CreateElement(XmlCollector);
collectorElement.SetAttribute(XmlDirectory, collector.CollectPath); collectorElement.SetAttribute(XmlDirectory, collector.CollectPath);
collectorElement.SetAttribute(XmlAddressRule, collector.AddressRuleName);
collectorElement.SetAttribute(XmlPackRule, collector.PackRuleName); collectorElement.SetAttribute(XmlPackRule, collector.PackRuleName);
collectorElement.SetAttribute(XmlFilterRule, collector.FilterRuleName); collectorElement.SetAttribute(XmlFilterRule, collector.FilterRuleName);
collectorElement.SetAttribute(XmlNotWriteToAssetList, collector.NotWriteToAssetList.ToString()); collectorElement.SetAttribute(XmlNotWriteToAssetList, collector.NotWriteToAssetList.ToString());

View File

@ -8,6 +8,11 @@ namespace YooAsset.Editor
{ {
public class AssetBundleGrouperSetting : ScriptableObject public class AssetBundleGrouperSetting : ScriptableObject
{ {
/// <summary>
/// 是否启用可寻址资源定位
/// </summary>
public bool EnableAddressable = false;
/// <summary> /// <summary>
/// 自动收集着色器 /// 自动收集着色器
/// </summary> /// </summary>
@ -40,6 +45,7 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public List<CollectAssetInfo> GetAllCollectAssets() public List<CollectAssetInfo> GetAllCollectAssets()
{ {
Dictionary<string, string> adressTemper = new Dictionary<string, string>(10000);
Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(10000); Dictionary<string, CollectAssetInfo> result = new Dictionary<string, CollectAssetInfo>(10000);
foreach (var grouper in Groupers) foreach (var grouper in Groupers)
{ {
@ -56,6 +62,24 @@ namespace YooAsset.Editor
} }
} }
} }
// 检测可寻址地址是否重复
if (EnableAddressable)
{
foreach (var collectInfo in result)
{
string address = collectInfo.Value.Address;
if (adressTemper.ContainsKey(address) == false)
{
adressTemper.Add(address, address);
}
else
{
throw new Exception($"The address is existed : {address}");
}
}
}
return result.Values.ToList(); return result.Values.ToList();
} }
} }

View File

@ -9,6 +9,9 @@ namespace YooAsset.Editor
{ {
public class AssetBundleGrouperSettingData public class AssetBundleGrouperSettingData
{ {
private static readonly Dictionary<string, System.Type> _cacheAddressRuleTypes = new Dictionary<string, System.Type>();
private static readonly Dictionary<string, IAddressRule> _cacheAddressRuleInstance = new Dictionary<string, IAddressRule>();
private static readonly Dictionary<string, System.Type> _cachePackRuleTypes = new Dictionary<string, System.Type>(); private static readonly Dictionary<string, System.Type> _cachePackRuleTypes = new Dictionary<string, System.Type>();
private static readonly Dictionary<string, IPackRule> _cachePackRuleInstance = new Dictionary<string, IPackRule>(); private static readonly Dictionary<string, IPackRule> _cachePackRuleInstance = new Dictionary<string, IPackRule>();
@ -32,6 +35,18 @@ namespace YooAsset.Editor
} }
} }
public static List<string> GetAddressRuleNames()
{
if (_setting == null)
LoadSettingData();
List<string> names = new List<string>();
foreach (var pair in _cacheAddressRuleTypes)
{
names.Add(pair.Key);
}
return names;
}
public static List<string> GetPackRuleNames() public static List<string> GetPackRuleNames()
{ {
if (_setting == null) if (_setting == null)
@ -56,6 +71,15 @@ namespace YooAsset.Editor
} }
return names; return names;
} }
public static bool HasAddressRuleName(string ruleName)
{
foreach (var pair in _cacheAddressRuleTypes)
{
if (pair.Key == ruleName)
return true;
}
return false;
}
public static bool HasPackRuleName(string ruleName) public static bool HasPackRuleName(string ruleName)
{ {
foreach (var pair in _cachePackRuleTypes) foreach (var pair in _cachePackRuleTypes)
@ -75,6 +99,7 @@ namespace YooAsset.Editor
return false; return false;
} }
/// <summary> /// <summary>
/// 加载配置文件 /// 加载配置文件
/// </summary> /// </summary>
@ -148,6 +173,31 @@ namespace YooAsset.Editor
_cacheFilterRuleTypes.Add(type.Name, type); _cacheFilterRuleTypes.Add(type.Name, type);
} }
} }
// IAddressRule
{
// 清空缓存集合
_cacheAddressRuleTypes.Clear();
_cacheAddressRuleInstance.Clear();
// 获取所有类型
List<Type> types = new List<Type>(100)
{
typeof(AddressByFileName),
typeof(AddressByCollectorAndFileName),
typeof(AddressByGrouperAndFileName)
};
TypeCache.TypeCollection collection = TypeCache.GetTypesDerivedFrom<IAddressRule>();
var customTypes = collection.ToList();
types.AddRange(customTypes);
for (int i = 0; i < types.Count; i++)
{
Type type = types[i];
if (_cacheAddressRuleTypes.ContainsKey(type.Name) == false)
_cacheAddressRuleTypes.Add(type.Name, type);
}
}
} }
/// <summary> /// <summary>
@ -176,6 +226,23 @@ namespace YooAsset.Editor
} }
// 实例类相关 // 实例类相关
public static IAddressRule GetAddressRuleInstance(string ruleName)
{
if (_cacheAddressRuleInstance.TryGetValue(ruleName, out IAddressRule instance))
return instance;
// 如果不存在创建类的实例
if (_cacheAddressRuleTypes.TryGetValue(ruleName, out Type type))
{
instance = (IAddressRule)Activator.CreateInstance(type);
_cacheAddressRuleInstance.Add(ruleName, instance);
return instance;
}
else
{
throw new Exception($"{nameof(IAddressRule)}类型无效:{ruleName}");
}
}
public static IPackRule GetPackRuleInstance(string ruleName) public static IPackRule GetPackRuleInstance(string ruleName)
{ {
if (_cachePackRuleInstance.TryGetValue(ruleName, out IPackRule instance)) if (_cachePackRuleInstance.TryGetValue(ruleName, out IPackRule instance))
@ -211,6 +278,13 @@ namespace YooAsset.Editor
} }
} }
// 可寻址编辑相关
public static void ModifyAddressable(bool enableAddressable)
{
Setting.EnableAddressable = enableAddressable;
IsDirty = true;
}
// 着色器编辑相关 // 着色器编辑相关
public static void ModifyShader(bool isCollectAllShaders, string shadersBundleName) public static void ModifyShader(bool isCollectAllShaders, string shadersBundleName)
{ {
@ -220,12 +294,10 @@ namespace YooAsset.Editor
} }
// 资源分组编辑相关 // 资源分组编辑相关
public static void CreateGrouper(string grouperName, string grouperDesc, string assetTags) public static void CreateGrouper(string grouperName)
{ {
AssetBundleGrouper grouper = new AssetBundleGrouper(); AssetBundleGrouper grouper = new AssetBundleGrouper();
grouper.GrouperName = grouperName; grouper.GrouperName = grouperName;
grouper.GrouperDesc = grouperDesc;
grouper.AssetTags = assetTags;
Setting.Groupers.Add(grouper); Setting.Groupers.Add(grouper);
IsDirty = true; IsDirty = true;
} }
@ -249,13 +321,13 @@ namespace YooAsset.Editor
} }
// 资源收集器编辑相关 // 资源收集器编辑相关
public static void CreateCollector(AssetBundleGrouper grouper, string collectPath, string packRuleName, string filterRuleName, bool notWriteToAssetList) public static void CreateCollector(AssetBundleGrouper grouper, string collectPath, string addressRuleName, string packRuleName, string filterRuleName)
{ {
AssetBundleCollector collector = new AssetBundleCollector(); AssetBundleCollector collector = new AssetBundleCollector();
collector.CollectPath = collectPath; collector.CollectPath = collectPath;
collector.AddressRuleName = addressRuleName;
collector.PackRuleName = packRuleName; collector.PackRuleName = packRuleName;
collector.FilterRuleName = filterRuleName; collector.FilterRuleName = filterRuleName;
collector.NotWriteToAssetList = notWriteToAssetList;
grouper.Collectors.Add(collector); grouper.Collectors.Add(collector);
IsDirty = true; IsDirty = true;
} }

View File

@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using UnityEditor;
namespace YooAsset.Editor
{
public static class AssetBundleGrouperSettingHelper
{
/// <summary>
/// 收集着色器的资源包名称
/// </summary>
public static string CollectShaderBundleName(string assetPath)
{
// 如果自动收集所有的着色器
if (AssetBundleGrouperSettingData.Setting.AutoCollectShaders)
{
System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
if (assetType == typeof(UnityEngine.Shader))
{
string bundleName = AssetBundleGrouperSettingData.Setting.ShadersBundleName;
return CorrectBundleName(bundleName, false);
}
}
return null;
}
/// <summary>
/// 修正资源包名称
/// </summary>
public static string CorrectBundleName(string bundleName, bool isRawBundle)
{
if (isRawBundle)
{
string fullName = $"{bundleName}.{YooAssetSettingsData.Setting.RawFileVariant}";
return EditorTools.GetRegularPath(fullName).ToLower();
}
else
{
string fullName = $"{bundleName}.{YooAssetSettingsData.Setting.AssetBundleFileVariant}";
return EditorTools.GetRegularPath(fullName).ToLower(); ;
}
}
#region 编辑器下运行时支持
private static readonly Dictionary<string, CollectAssetInfo> _locationDic = new Dictionary<string, CollectAssetInfo>(1000);
public static void InitEditorPlayMode(bool enableAddressable)
{
_locationDic.Clear();
if (enableAddressable)
{
var collectAssetList = AssetBundleGrouperSettingData.Setting.GetAllCollectAssets();
foreach (var collectAsset in collectAssetList)
{
if (collectAsset.NotWriteToAssetList)
continue;
string address = collectAsset.Address;
if (_locationDic.ContainsKey(address))
UnityEngine.Debug.LogWarning($"Address have existed : {address}");
else
_locationDic.Add(address, collectAsset);
}
}
else
{
var collectAssetList = AssetBundleGrouperSettingData.Setting.GetAllCollectAssets();
foreach (var collectAsset in collectAssetList)
{
if (collectAsset.NotWriteToAssetList)
continue;
// 添加原始路径
string assetPath = collectAsset.AssetPath;
if (_locationDic.ContainsKey(assetPath))
UnityEngine.Debug.LogWarning($"Asset path have existed : {assetPath}");
else
_locationDic.Add(assetPath, collectAsset);
// 添加去掉后缀名的路径
if (Path.HasExtension(assetPath))
{
string assetPathWithoutExtension = StringUtility.RemoveExtension(assetPath);
if (_locationDic.ContainsKey(assetPathWithoutExtension))
UnityEngine.Debug.LogWarning($"Asset path have existed : {assetPathWithoutExtension}");
else
_locationDic.Add(assetPathWithoutExtension, collectAsset);
}
}
}
}
public static string ConvertLocationToAssetPath(string location)
{
// 检测地址合法性
CheckLocation(location);
if (_locationDic.ContainsKey(location))
{
return _locationDic[location].AssetPath;
}
else
{
UnityEngine.Debug.LogWarning($"Not found asset in grouper setting : {location}");
return string.Empty;
}
}
private static void CheckLocation(string location)
{
if (string.IsNullOrEmpty(location))
{
UnityEngine.Debug.LogError("location param is null or empty!");
}
else
{
// 检查路径末尾是否有空格
int index = location.LastIndexOf(" ");
if (index != -1)
{
if (location.Length == index + 1)
UnityEngine.Debug.LogWarning($"Found blank character in location : \"{location}\"");
}
if (location.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
UnityEngine.Debug.LogWarning($"Found illegal character in location : \"{location}\"");
}
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f94918fa1ea63c34fa0e49fdad4119cf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -19,10 +19,12 @@ namespace YooAsset.Editor
window.minSize = new Vector2(800, 600); window.minSize = new Vector2(800, 600);
} }
private List<string> _addressRuleList;
private List<string> _packRuleList; private List<string> _packRuleList;
private List<string> _filterRuleList; private List<string> _filterRuleList;
private ListView _grouperListView; private ListView _grouperListView;
private ScrollView _collectorScrollView; private ScrollView _collectorScrollView;
private Toggle _enableAddressableToogle;
private Toggle _autoCollectShaderToogle; private Toggle _autoCollectShaderToogle;
private TextField _shaderBundleNameTxt; private TextField _shaderBundleNameTxt;
private TextField _grouperNameTxt; private TextField _grouperNameTxt;
@ -37,6 +39,7 @@ namespace YooAsset.Editor
VisualElement root = this.rootVisualElement; VisualElement root = this.rootVisualElement;
_addressRuleList = AssetBundleGrouperSettingData.GetAddressRuleNames();
_packRuleList = AssetBundleGrouperSettingData.GetPackRuleNames(); _packRuleList = AssetBundleGrouperSettingData.GetPackRuleNames();
_filterRuleList = AssetBundleGrouperSettingData.GetFilterRuleNames(); _filterRuleList = AssetBundleGrouperSettingData.GetFilterRuleNames();
@ -59,7 +62,12 @@ namespace YooAsset.Editor
var importBtn = root.Q<Button>("ImportButton"); var importBtn = root.Q<Button>("ImportButton");
importBtn.clicked += ImportBtn_clicked; importBtn.clicked += ImportBtn_clicked;
// 着色器相关 // 公共设置相关
_enableAddressableToogle = root.Q<Toggle>("EnableAddressable");
_enableAddressableToogle.RegisterValueChangedCallback(evt =>
{
AssetBundleGrouperSettingData.ModifyAddressable(evt.newValue);
});
_autoCollectShaderToogle = root.Q<Toggle>("AutoCollectShader"); _autoCollectShaderToogle = root.Q<Toggle>("AutoCollectShader");
_autoCollectShaderToogle.RegisterValueChangedCallback(evt => _autoCollectShaderToogle.RegisterValueChangedCallback(evt =>
{ {
@ -157,6 +165,7 @@ namespace YooAsset.Editor
// 刷新窗体 // 刷新窗体
private void RefreshWindow() private void RefreshWindow()
{ {
_enableAddressableToogle.SetValueWithoutNotify(AssetBundleGrouperSettingData.Setting.EnableAddressable);
_autoCollectShaderToogle.SetValueWithoutNotify(AssetBundleGrouperSettingData.Setting.AutoCollectShaders); _autoCollectShaderToogle.SetValueWithoutNotify(AssetBundleGrouperSettingData.Setting.AutoCollectShaders);
_shaderBundleNameTxt.SetEnabled(AssetBundleGrouperSettingData.Setting.AutoCollectShaders); _shaderBundleNameTxt.SetEnabled(AssetBundleGrouperSettingData.Setting.AutoCollectShaders);
_shaderBundleNameTxt.SetValueWithoutNotify(AssetBundleGrouperSettingData.Setting.ShadersBundleName); _shaderBundleNameTxt.SetValueWithoutNotify(AssetBundleGrouperSettingData.Setting.ShadersBundleName);
@ -225,7 +234,7 @@ namespace YooAsset.Editor
private void AddGrouperBtn_clicked() private void AddGrouperBtn_clicked()
{ {
Undo.RecordObject(AssetBundleGrouperSettingData.Setting, "YooAsset AddGrouper"); Undo.RecordObject(AssetBundleGrouperSettingData.Setting, "YooAsset AddGrouper");
AssetBundleGrouperSettingData.CreateGrouper("Default Grouper", string.Empty, string.Empty); AssetBundleGrouperSettingData.CreateGrouper("Default Grouper");
FillGrouperViewData(); FillGrouperViewData();
} }
private void RemoveGrouperBtn_clicked() private void RemoveGrouperBtn_clicked()
@ -259,9 +268,7 @@ namespace YooAsset.Editor
_collectorScrollView.Clear(); _collectorScrollView.Clear();
for (int i = 0; i < selectGrouper.Collectors.Count; i++) for (int i = 0; i < selectGrouper.Collectors.Count; i++)
{ {
var collector = selectGrouper.Collectors[i];
VisualElement element = MakeCollectorListViewItem(); VisualElement element = MakeCollectorListViewItem();
collector.UserData = element;
BindCollectorListViewItem(element, i); BindCollectorListViewItem(element, i);
_collectorScrollView.Add(element); _collectorScrollView.Add(element);
} }
@ -278,11 +285,23 @@ namespace YooAsset.Editor
elementBottom.style.flexDirection = FlexDirection.Row; elementBottom.style.flexDirection = FlexDirection.Row;
element.Add(elementBottom); element.Add(elementBottom);
VisualElement elementFold = new VisualElement(); VisualElement elementFoldout = new VisualElement();
elementFold.style.flexDirection = FlexDirection.Row; elementFoldout.style.flexDirection = FlexDirection.Row;
element.Add(elementFold); element.Add(elementFoldout);
VisualElement elementSpace = new VisualElement();
elementSpace.style.flexDirection = FlexDirection.Column;
element.Add(elementSpace);
// Top VisualElement // Top VisualElement
{
var button = new Button();
button.name = "Button1";
button.text = "-";
button.style.unityTextAlign = TextAnchor.MiddleCenter;
button.style.flexGrow = 0f;
elementTop.Add(button);
}
{ {
var objectField = new ObjectField(); var objectField = new ObjectField();
objectField.name = "ObjectField1"; objectField.name = "ObjectField1";
@ -292,33 +311,33 @@ namespace YooAsset.Editor
objectField.style.flexGrow = 1f; objectField.style.flexGrow = 1f;
elementTop.Add(objectField); elementTop.Add(objectField);
var label = objectField.Q<Label>(); var label = objectField.Q<Label>();
label.style.minWidth = 80; label.style.minWidth = 63;
}
{
var button = new Button();
button.name = "Button1";
button.text = "[ - ]";
button.style.unityTextAlign = TextAnchor.MiddleCenter;
button.style.flexGrow = 0f;
elementTop.Add(button);
} }
// Bottom VisualElement // Bottom VisualElement
{ {
var label = new Label(); var label = new Label();
label.style.width = 80; label.style.width = 90;
elementBottom.Add(label); elementBottom.Add(label);
} }
if (_enableAddressableToogle.value)
{
var popupField = new PopupField<string>(_addressRuleList, 0);
popupField.name = "PopupField1";
popupField.style.unityTextAlign = TextAnchor.MiddleLeft;
popupField.style.width = 200;
elementBottom.Add(popupField);
}
{ {
var popupField = new PopupField<string>(_packRuleList, 0); var popupField = new PopupField<string>(_packRuleList, 0);
popupField.name = "PopupField1"; popupField.name = "PopupField2";
popupField.style.unityTextAlign = TextAnchor.MiddleLeft; popupField.style.unityTextAlign = TextAnchor.MiddleLeft;
popupField.style.width = 150; popupField.style.width = 150;
elementBottom.Add(popupField); elementBottom.Add(popupField);
} }
{ {
var popupField = new PopupField<string>(_filterRuleList, 0); var popupField = new PopupField<string>(_filterRuleList, 0);
popupField.name = "PopupField2"; popupField.name = "PopupField3";
popupField.style.unityTextAlign = TextAnchor.MiddleLeft; popupField.style.unityTextAlign = TextAnchor.MiddleLeft;
popupField.style.width = 150; popupField.style.width = 150;
elementBottom.Add(popupField); elementBottom.Add(popupField);
@ -346,6 +365,27 @@ namespace YooAsset.Editor
label.style.minWidth = 40; label.style.minWidth = 40;
} }
// Foldout VisualElement
{
var label = new Label();
label.style.width = 90;
elementFoldout.Add(label);
}
{
var foldout = new Foldout();
foldout.name = "Foldout1";
foldout.value = false;
foldout.text = "Assets";
elementFoldout.Add(foldout);
}
// Space VisualElement
{
var label = new Label();
label.style.height = 10;
elementSpace.Add(label);
}
return element; return element;
} }
private void BindCollectorListViewItem(VisualElement element, int index) private void BindCollectorListViewItem(VisualElement element, int index)
@ -355,12 +395,21 @@ namespace YooAsset.Editor
return; return;
var collector = selectGrouper.Collectors[index]; var collector = selectGrouper.Collectors[index];
collector.UserData = element;
var collectObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(collector.CollectPath); var collectObject = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(collector.CollectPath);
if (collectObject != null) if (collectObject != null)
collectObject.name = collector.CollectPath; collectObject.name = collector.CollectPath;
// Foldout
var foldout = element.Q<Foldout>("Foldout1");
RefreshFoldout(foldout, selectGrouper, collector);
// Remove Button
var removeBtn = element.Q<Button>("Button1");
removeBtn.clicked += () =>
{
RemoveCollectorBtn_clicked(collector);
};
// Collect Path // Collect Path
var objectField1 = element.Q<ObjectField>("ObjectField1"); var objectField1 = element.Q<ObjectField>("ObjectField1");
objectField1.SetValueWithoutNotify(collectObject); objectField1.SetValueWithoutNotify(collectObject);
@ -369,31 +418,40 @@ namespace YooAsset.Editor
collector.CollectPath = AssetDatabase.GetAssetPath(evt.newValue); collector.CollectPath = AssetDatabase.GetAssetPath(evt.newValue);
objectField1.value.name = collector.CollectPath; objectField1.value.name = collector.CollectPath;
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector); AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
RefreshFoldout(foldout, selectGrouper, collector);
}); });
// Remove Button // Address Rule
var removeBtn = element.Q<Button>("Button1"); var popupField1 = element.Q<PopupField<string>>("PopupField1");
removeBtn.clicked += ()=> if (popupField1 != null)
{ {
RemoveCollectorBtn_clicked(collector); popupField1.index = GetAddressRuleIndex(collector.AddressRuleName);
}; popupField1.RegisterValueChangedCallback(evt =>
{
collector.AddressRuleName = evt.newValue;
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
RefreshFoldout(foldout, selectGrouper, collector);
});
}
// Pack Rule // Pack Rule
var popupField1 = element.Q<PopupField<string>>("PopupField1"); var popupField2 = element.Q<PopupField<string>>("PopupField2");
popupField1.index = GetPackRuleIndex(collector.PackRuleName); popupField2.index = GetPackRuleIndex(collector.PackRuleName);
popupField1.RegisterValueChangedCallback(evt => popupField2.RegisterValueChangedCallback(evt =>
{ {
collector.PackRuleName = evt.newValue; collector.PackRuleName = evt.newValue;
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector); AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
RefreshFoldout(foldout, selectGrouper, collector);
}); });
// Filter Rule // Filter Rule
var popupField2 = element.Q<PopupField<string>>("PopupField2"); var popupField3 = element.Q<PopupField<string>>("PopupField3");
popupField2.index = GetFilterRuleIndex(collector.FilterRuleName); popupField3.index = GetFilterRuleIndex(collector.FilterRuleName);
popupField2.RegisterValueChangedCallback(evt => popupField3.RegisterValueChangedCallback(evt =>
{ {
collector.FilterRuleName = evt.newValue; collector.FilterRuleName = evt.newValue;
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector); AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
RefreshFoldout(foldout, selectGrouper, collector);
}); });
// NotWriteToAssetList // NotWriteToAssetList
@ -403,6 +461,7 @@ namespace YooAsset.Editor
{ {
collector.NotWriteToAssetList = evt.newValue; collector.NotWriteToAssetList = evt.newValue;
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector); AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
RefreshFoldout(foldout, selectGrouper, collector);
}); });
// Tags // Tags
@ -414,13 +473,48 @@ namespace YooAsset.Editor
AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector); AssetBundleGrouperSettingData.ModifyCollector(selectGrouper, collector);
}); });
} }
private void RefreshFoldout(Foldout foldout, AssetBundleGrouper grouper, AssetBundleCollector collector)
{
// 清空旧元素
foldout.Clear();
if (collector.IsValid() && collector.NotWriteToAssetList == false)
{
var collectAssetInfos = collector.GetAllCollectAssets(grouper);
foreach (var collectAssetInfo in collectAssetInfos)
{
VisualElement elementRow = new VisualElement();
elementRow.style.flexDirection = FlexDirection.Row;
foldout.Add(elementRow);
string showInfo = collectAssetInfo.AssetPath;
if (_enableAddressableToogle.value)
{
IAddressRule instance = AssetBundleGrouperSettingData.GetAddressRuleInstance(collector.AddressRuleName);
AddressRuleData ruleData = new AddressRuleData(collectAssetInfo.AssetPath, collector.CollectPath, grouper.GrouperName);
string addressValue = instance.GetAssetAddress(ruleData);
showInfo = $"[{addressValue}] {showInfo}";
}
var label = new Label();
label.text = showInfo;
label.style.width = 300;
label.style.marginLeft = 0;
label.style.flexGrow = 1;
elementRow.Add(label);
}
}
}
private void AddCollectorBtn_clicked() private void AddCollectorBtn_clicked()
{ {
var selectGrouper = _grouperListView.selectedItem as AssetBundleGrouper; var selectGrouper = _grouperListView.selectedItem as AssetBundleGrouper;
if (selectGrouper == null) if (selectGrouper == null)
return; return;
AssetBundleGrouperSettingData.CreateCollector(selectGrouper, string.Empty, nameof(PackDirectory), nameof(CollectAll), false); string addressRuleName = nameof(AddressByFileName);
string packRuleName = nameof(PackDirectory);
string filterRuleName = nameof(CollectAll);
AssetBundleGrouperSettingData.CreateCollector(selectGrouper, string.Empty, addressRuleName, packRuleName, filterRuleName);
FillCollectorViewData(); FillCollectorViewData();
} }
private void RemoveCollectorBtn_clicked(AssetBundleCollector selectCollector) private void RemoveCollectorBtn_clicked(AssetBundleCollector selectCollector)
@ -434,20 +528,29 @@ namespace YooAsset.Editor
FillCollectorViewData(); FillCollectorViewData();
} }
private int GetPackRuleIndex(string packRuleName) private int GetAddressRuleIndex(string ruleName)
{ {
for (int i = 0; i < _packRuleList.Count; i++) for (int i = 0; i < _addressRuleList.Count; i++)
{ {
if (_packRuleList[i] == packRuleName) if (_addressRuleList[i] == ruleName)
return i; return i;
} }
return 0; return 0;
} }
private int GetFilterRuleIndex(string filterRuleName) private int GetPackRuleIndex(string ruleName)
{
for (int i = 0; i < _packRuleList.Count; i++)
{
if (_packRuleList[i] == ruleName)
return i;
}
return 0;
}
private int GetFilterRuleIndex(string ruleName)
{ {
for (int i = 0; i < _filterRuleList.Count; i++) for (int i = 0; i < _filterRuleList.Count; i++)
{ {
if (_filterRuleList[i] == filterRuleName) if (_filterRuleList[i] == ruleName)
return i; return i;
} }
return 0; return 0;

View File

@ -13,7 +13,8 @@
</ui:VisualElement> </ui:VisualElement>
</ui:VisualElement> </ui:VisualElement>
<ui:VisualElement name="RightContainer" style="flex-direction: column; flex-grow: 1;"> <ui:VisualElement name="RightContainer" style="flex-direction: column; flex-grow: 1;">
<ui:VisualElement name="ShaderContainer" style="height: 30px; background-color: rgb(67, 67, 67); flex-direction: row; border-left-width: 5px; border-right-width: 5px; border-top-width: 5px; border-bottom-width: 5px;"> <ui:VisualElement name="PublicContainer" style="height: 30px; background-color: rgb(67, 67, 67); flex-direction: row; border-left-width: 5px; border-right-width: 5px; border-top-width: 5px; border-bottom-width: 5px;">
<ui:Toggle label="Enable Addressable" name="EnableAddressable" style="width: 196px; -unity-text-align: middle-left;" />
<ui:Toggle label="Auto Collect Shaders" name="AutoCollectShader" style="width: 196px; -unity-text-align: middle-left;" /> <ui:Toggle label="Auto Collect Shaders" name="AutoCollectShader" style="width: 196px; -unity-text-align: middle-left;" />
<ui:TextField picking-mode="Ignore" label="Shader Bundle Name" name="ShaderBundleName" style="flex-grow: 1; -unity-text-align: middle-left;" /> <ui:TextField picking-mode="Ignore" label="Shader Bundle Name" name="ShaderBundleName" style="flex-grow: 1; -unity-text-align: middle-left;" />
</ui:VisualElement> </ui:VisualElement>

View File

@ -10,6 +10,11 @@ namespace YooAsset.Editor
/// </summary> /// </summary>
public string BundleName { private set; get; } public string BundleName { private set; get; }
/// <summary>
/// 可寻址地址
/// </summary>
public string Address { private set; get; }
/// <summary> /// <summary>
/// 资源路径 /// 资源路径
/// </summary> /// </summary>
@ -36,17 +41,10 @@ namespace YooAsset.Editor
public List<string> DependAssets = new List<string>(); public List<string> DependAssets = new List<string>();
public CollectAssetInfo(string bundleName, string assetPath, List<string> assetTags, bool isRawAsset, bool notWriteToAssetList) public CollectAssetInfo(string bundleName, string address, string assetPath, List<string> assetTags, bool isRawAsset, bool notWriteToAssetList)
{ {
BundleName = bundleName; BundleName = bundleName;
AssetPath = assetPath; Address = address;
AssetTags = assetTags;
IsRawAsset = isRawAsset;
NotWriteToAssetList = notWriteToAssetList;
}
public CollectAssetInfo(string assetPath, List<string> assetTags, bool isRawAsset, bool notWriteToAssetList)
{
BundleName = string.Empty;
AssetPath = assetPath; AssetPath = assetPath;
AssetTags = assetTags; AssetTags = assetTags;
IsRawAsset = isRawAsset; IsRawAsset = isRawAsset;

View File

@ -0,0 +1,40 @@
using System.IO;
namespace YooAsset.Editor
{
/// <summary>
/// 以文件名为定位地址
/// </summary>
public class AddressByFileName : IAddressRule
{
string IAddressRule.GetAssetAddress(AddressRuleData data)
{
return Path.GetFileNameWithoutExtension(data.AssetPath);
}
}
/// <summary>
/// 以组名+文件名为定位地址
/// </summary>
public class AddressByGrouperAndFileName : IAddressRule
{
string IAddressRule.GetAssetAddress(AddressRuleData data)
{
string fileName = Path.GetFileNameWithoutExtension(data.AssetPath);
return $"{data.GrouperName}_{fileName}";
}
}
/// <summary>
/// 以收集器名+文件名为定位地址
/// </summary>
public class AddressByCollectorAndFileName : IAddressRule
{
string IAddressRule.GetAssetAddress(AddressRuleData data)
{
string fileName = Path.GetFileNameWithoutExtension(data.AssetPath);
string collectorName = Path.GetFileNameWithoutExtension(data.CollectPath);
return $"{collectorName}_{fileName}";
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6df37bfd87103a54ca60c0c467a5f33b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,25 @@

namespace YooAsset.Editor
{
public struct AddressRuleData
{
public string AssetPath;
public string CollectPath;
public string GrouperName;
public AddressRuleData(string assetPath, string collectPath, string grouperName)
{
AssetPath = assetPath;
CollectPath = collectPath;
GrouperName = grouperName;
}
}
/// <summary>
/// 寻址规则接口
/// </summary>
public interface IAddressRule
{
string GetAssetAddress(AddressRuleData data);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 426a4ff47699b6844946329f54a89128
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -67,6 +67,7 @@ namespace YooAsset.Editor
_items.Add(new ItemWrapper("构建耗时", $"{buildReport.Summary.BuildSeconds}秒")); _items.Add(new ItemWrapper("构建耗时", $"{buildReport.Summary.BuildSeconds}秒"));
_items.Add(new ItemWrapper("构建平台", $"{buildReport.Summary.BuildTarget}")); _items.Add(new ItemWrapper("构建平台", $"{buildReport.Summary.BuildTarget}"));
_items.Add(new ItemWrapper("构建版本", $"{buildReport.Summary.BuildVersion}")); _items.Add(new ItemWrapper("构建版本", $"{buildReport.Summary.BuildVersion}"));
_items.Add(new ItemWrapper("启用可寻址资源定位", $"{buildReport.Summary.EnableAddressable}"));
_items.Add(new ItemWrapper("启用自动分包机制", $"{buildReport.Summary.EnableAutoCollect}")); _items.Add(new ItemWrapper("启用自动分包机制", $"{buildReport.Summary.EnableAutoCollect}"));
_items.Add(new ItemWrapper("追加文件扩展名", $"{buildReport.Summary.AppendFileExtension}")); _items.Add(new ItemWrapper("追加文件扩展名", $"{buildReport.Summary.AppendFileExtension}"));
_items.Add(new ItemWrapper("自动收集着色器", $"{buildReport.Summary.AutoCollectShaders}")); _items.Add(new ItemWrapper("自动收集着色器", $"{buildReport.Summary.AutoCollectShaders}"));

View File

@ -8,6 +8,7 @@ namespace YooAsset
internal sealed class BundledSceneProvider : BundledProvider internal sealed class BundledSceneProvider : BundledProvider
{ {
public readonly LoadSceneMode SceneMode; public readonly LoadSceneMode SceneMode;
private readonly string _sceneName;
private readonly bool _activateOnLoad; private readonly bool _activateOnLoad;
private readonly int _priority; private readonly int _priority;
private AsyncOperation _asyncOp; private AsyncOperation _asyncOp;
@ -25,6 +26,7 @@ namespace YooAsset
: base(scenePath, null) : base(scenePath, null)
{ {
SceneMode = sceneMode; SceneMode = sceneMode;
_sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath);
_activateOnLoad = activateOnLoad; _activateOnLoad = activateOnLoad;
_priority = priority; _priority = priority;
} }
@ -68,7 +70,7 @@ namespace YooAsset
// 2. 加载场景 // 2. 加载场景
if (Status == EStatus.Loading) if (Status == EStatus.Loading)
{ {
_asyncOp = SceneManager.LoadSceneAsync(AssetName, SceneMode); _asyncOp = SceneManager.LoadSceneAsync(_sceneName, SceneMode);
if (_asyncOp != null) if (_asyncOp != null)
{ {
_asyncOp.allowSceneActivation = true; _asyncOp.allowSceneActivation = true;
@ -78,7 +80,7 @@ namespace YooAsset
else else
{ {
Status = EStatus.Fail; Status = EStatus.Fail;
LastError = $"Failed to load scene : {AssetName}"; LastError = $"Failed to load scene : {_sceneName}";
YooLogger.Error(LastError); YooLogger.Error(LastError);
InvokeCompletion(); InvokeCompletion();
} }
@ -89,7 +91,7 @@ namespace YooAsset
{ {
if (_asyncOp.isDone) if (_asyncOp.isDone)
{ {
SceneObject = SceneManager.GetSceneByName(AssetName); SceneObject = SceneManager.GetSceneByName(_sceneName);
if (SceneObject.IsValid() && _activateOnLoad) if (SceneObject.IsValid() && _activateOnLoad)
SceneManager.SetActiveScene(SceneObject); SceneManager.SetActiveScene(SceneObject);

View File

@ -5,6 +5,11 @@ namespace YooAsset
[Serializable] [Serializable]
internal class PatchAsset internal class PatchAsset
{ {
/// <summary>
/// 可寻址地址
/// </summary>
public string Address;
/// <summary> /// <summary>
/// 资源路径 /// 资源路径
/// </summary> /// </summary>

View File

@ -12,6 +12,11 @@ namespace YooAsset
[Serializable] [Serializable]
internal class PatchManifest internal class PatchManifest
{ {
/// <summary>
/// 启用可寻址资源定位
/// </summary>
public bool EnableAddressable;
/// <summary> /// <summary>
/// 资源版本号 /// 资源版本号
/// </summary> /// </summary>
@ -45,6 +50,12 @@ namespace YooAsset
[NonSerialized] [NonSerialized]
public readonly Dictionary<string, PatchAsset> Assets = new Dictionary<string, PatchAsset>(); public readonly Dictionary<string, PatchAsset> Assets = new Dictionary<string, PatchAsset>();
/// <summary>
/// 可寻址地址映射集合
/// </summary>
[NonSerialized]
public readonly Dictionary<string, string> AddressDic = new Dictionary<string, string>();
/// <summary> /// <summary>
/// 获取内置资源标签列表 /// 获取内置资源标签列表
@ -108,6 +119,22 @@ namespace YooAsset
} }
} }
/// <summary>
/// 可寻址地址转换为资源路径
/// </summary>
public string ConvertAddress(string address)
{
if (AddressDic.TryGetValue(address, out string assetPath))
{
return assetPath;
}
else
{
YooLogger.Warning($"Not found address in patch manifest : {address}");
return string.Empty;
}
}
/// <summary> /// <summary>
/// 序列化 /// 序列化
@ -155,6 +182,19 @@ namespace YooAsset
} }
} }
// Address
if (patchManifest.EnableAddressable)
{
foreach (var patchAsset in patchManifest.AssetList)
{
string address = patchAsset.Address;
if (patchManifest.AddressDic.ContainsKey(address))
throw new Exception($"Address have existed : {address}");
else
patchManifest.AddressDic.Add(address, patchAsset.AssetPath);
}
}
return patchManifest; return patchManifest;
} }
} }

View File

@ -31,6 +31,10 @@ namespace YooAsset
BundleInfo bundleInfo = new BundleInfo(bundleName); BundleInfo bundleInfo = new BundleInfo(bundleName);
return bundleInfo; return bundleInfo;
} }
string IBundleServices.ConvertAddress(string address)
{
throw new Exception($"Editor play mode not support addressable.");
}
string IBundleServices.GetBundleName(string assetPath) string IBundleServices.GetBundleName(string assetPath)
{ {
return assetPath; return assetPath;

View File

@ -237,6 +237,10 @@ namespace YooAsset
return bundleInfo; return bundleInfo;
} }
} }
string IBundleServices.ConvertAddress(string address)
{
return LocalPatchManifest.ConvertAddress(address);
}
string IBundleServices.GetBundleName(string assetPath) string IBundleServices.GetBundleName(string assetPath)
{ {
return LocalPatchManifest.GetBundleName(assetPath); return LocalPatchManifest.GetBundleName(assetPath);

View File

@ -56,6 +56,10 @@ namespace YooAsset
return bundleInfo; return bundleInfo;
} }
} }
string IBundleServices.ConvertAddress(string address)
{
return AppPatchManifest.ConvertAddress(address);
}
string IBundleServices.GetBundleName(string assetPath) string IBundleServices.GetBundleName(string assetPath)
{ {
return AppPatchManifest.GetBundleName(assetPath); return AppPatchManifest.GetBundleName(assetPath);

View File

@ -1,11 +0,0 @@

namespace YooAsset
{
public class AddressLocationServices : ILocationServices
{
public string ConvertLocationToAssetPath(YooAssets.EPlayMode playMode, string location)
{
throw new System.NotImplementedException("该功能暂未支持!");
}
}
}

View File

@ -1,105 +0,0 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
public class DefaultLocationServices : ILocationServices
{
private readonly string _resourceRoot;
public DefaultLocationServices(string resourceRoot)
{
if (string.IsNullOrEmpty(resourceRoot) == false)
_resourceRoot = PathHelper.GetRegularPath(resourceRoot);
}
public string ConvertLocationToAssetPath(YooAssets.EPlayMode playMode, string location)
{
#if UNITY_EDITOR
CheckLocation(location);
#endif
if (playMode == YooAssets.EPlayMode.EditorPlayMode)
{
string filePath = CombineAssetPath(_resourceRoot, location);
return FindDatabaseAssetPath(filePath);
}
else
{
return CombineAssetPath(_resourceRoot, location);
}
}
/// <summary>
/// 合并资源路径
/// </summary>
private static string CombineAssetPath(string root, string location)
{
if (string.IsNullOrEmpty(root))
return location;
else
return $"{root}/{location}";
}
/// <summary>
/// 获取AssetDatabase的加载路径
/// </summary>
private static string FindDatabaseAssetPath(string filePath)
{
#if UNITY_EDITOR
if (File.Exists(filePath))
return filePath;
// AssetDatabase加载资源需要提供文件后缀格式然而资源定位地址并没有文件格式信息。
// 所以我们通过查找该文件所在文件夹内同名的首个文件来确定AssetDatabase的加载路径。
// 注意AssetDatabase.FindAssets() 返回文件内包括递归文件夹内所有资源的GUID
string fileName = Path.GetFileName(filePath);
string directory = PathHelper.GetDirectory(filePath);
string[] guids = UnityEditor.AssetDatabase.FindAssets(string.Empty, new[] { directory });
for (int i = 0; i < guids.Length; i++)
{
string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[i]);
if (UnityEditor.AssetDatabase.IsValidFolder(assetPath))
continue;
string assetDirectory = PathHelper.GetDirectory(assetPath);
if (assetDirectory != directory)
continue;
string assetName = Path.GetFileNameWithoutExtension(assetPath);
if (assetName == fileName)
return assetPath;
}
// 没有找到同名的资源文件
YooLogger.Warning($"Not found asset : {filePath}");
return filePath;
#else
throw new System.NotImplementedException();
#endif
}
#if UNITY_EDITOR
private void CheckLocation(string location)
{
if (string.IsNullOrEmpty(location))
{
YooLogger.Error("location param is null or empty!");
}
else
{
// 检查路径末尾是否有空格
int index = location.LastIndexOf(" ");
if (index != -1)
{
if (location.Length == index + 1)
YooLogger.Warning($"Found blank character in location : \"{location}\"");
}
if (location.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
YooLogger.Warning($"Found illegal character in location : \"{location}\"");
}
}
#endif
}
}

View File

@ -8,6 +8,11 @@ namespace YooAsset
/// </summary> /// </summary>
BundleInfo GetBundleInfo(string bundleName); BundleInfo GetBundleInfo(string bundleName);
/// <summary>
/// 可寻址地址转换为资源路径
/// </summary>
string ConvertAddress(string address);
/// <summary> /// <summary>
/// 获取资源所属的资源包名称 /// 获取资源所属的资源包名称
/// </summary> /// </summary>

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e400ee1e8b3556479bfa493ff7fe778
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@

namespace YooAsset
{
public class AddressLocationServices : ILocationServices
{
public AddressLocationServices()
{
#if UNITY_EDITOR
LocationServicesHelper.InitEditorPlayMode(true);
#endif
}
public string ConvertLocationToAssetPath(YooAssets.EPlayMode playMode, string location)
{
if (playMode == YooAssets.EPlayMode.EditorPlayMode)
{
#if UNITY_EDITOR
return LocationServicesHelper.ConvertLocationToAssetPath(location);
#else
throw new System.NotImplementedException();
#endif
}
else
{
return YooAssets.ConvertAddress(location);
}
}
}
}

View File

@ -0,0 +1,42 @@

namespace YooAsset
{
public class DefaultLocationServices : ILocationServices
{
private readonly string _resourceRoot;
public DefaultLocationServices(string resourceRoot)
{
if (string.IsNullOrEmpty(resourceRoot) == false)
_resourceRoot = PathHelper.GetRegularPath(resourceRoot);
#if UNITY_EDITOR
LocationServicesHelper.InitEditorPlayMode(false);
#endif
}
public string ConvertLocationToAssetPath(YooAssets.EPlayMode playMode, string location)
{
location = CombineAssetPath(_resourceRoot, location);
if (playMode == YooAssets.EPlayMode.EditorPlayMode)
{
#if UNITY_EDITOR
return LocationServicesHelper.ConvertLocationToAssetPath(location);
#else
throw new System.NotImplementedException();
#endif
}
else
{
return location;
}
}
private string CombineAssetPath(string root, string location)
{
if (string.IsNullOrEmpty(root))
return location;
else
return $"{root}/{location}";
}
}
}

View File

@ -0,0 +1,32 @@
#if UNITY_EDITOR
using System.Reflection;
namespace YooAsset
{
internal static class LocationServicesHelper
{
private static System.Type AssetBundleGrouperSettingHelperClassType;
public static void InitEditorPlayMode(bool enableAddressable)
{
AssetBundleGrouperSettingHelperClassType = Assembly.Load("YooAsset.Editor").GetType("YooAsset.Editor.AssetBundleGrouperSettingHelper");
InvokePublicStaticMethod(AssetBundleGrouperSettingHelperClassType, "InitEditorPlayMode", enableAddressable);
}
public static string ConvertLocationToAssetPath(string location)
{
return (string)InvokePublicStaticMethod(AssetBundleGrouperSettingHelperClassType, "ConvertLocationToAssetPath", location);
}
private static object InvokePublicStaticMethod(System.Type type, string method, params object[] parameters)
{
var methodInfo = type.GetMethod(method, BindingFlags.Public | BindingFlags.Static);
if (methodInfo == null)
{
UnityEngine.Debug.LogError($"{type.FullName} not found method : {method}");
return null;
}
return methodInfo.Invoke(null, parameters);
}
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b39dcdab07554ca4ab4ace5ca97aee78
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -377,18 +377,16 @@ namespace YooAsset
} }
else if (_playMode == EPlayMode.OfflinePlayMode) else if (_playMode == EPlayMode.OfflinePlayMode)
{ {
IBundleServices bundleServices = _offlinePlayModeImpl; string bundleName = _bundleServices.GetBundleName(assetPath);
string bundleName = bundleServices.GetBundleName(assetPath); BundleInfo bundleInfo = _bundleServices.GetBundleInfo(bundleName);
BundleInfo bundleInfo = bundleServices.GetBundleInfo(bundleName);
RawFileOperation operation = new OfflinePlayModeRawFileOperation(bundleInfo, copyPath); RawFileOperation operation = new OfflinePlayModeRawFileOperation(bundleInfo, copyPath);
OperationSystem.ProcessOperaiton(operation); OperationSystem.ProcessOperaiton(operation);
return operation; return operation;
} }
else if (_playMode == EPlayMode.HostPlayMode) else if (_playMode == EPlayMode.HostPlayMode)
{ {
IBundleServices bundleServices = _hostPlayModeImpl; string bundleName = _bundleServices.GetBundleName(assetPath);
string bundleName = bundleServices.GetBundleName(assetPath); BundleInfo bundleInfo = _bundleServices.GetBundleInfo(bundleName);
BundleInfo bundleInfo = bundleServices.GetBundleInfo(bundleName);
RawFileOperation operation = new HostPlayModeRawFileOperation(bundleInfo, copyPath); RawFileOperation operation = new HostPlayModeRawFileOperation(bundleInfo, copyPath);
OperationSystem.ProcessOperaiton(operation); OperationSystem.ProcessOperaiton(operation);
return operation; return operation;
@ -650,9 +648,6 @@ namespace YooAsset
#endregion #endregion
#region 内部方法 #region 内部方法
/// <summary>
/// 更新资源系统
/// </summary>
internal static void InternalUpdate() internal static void InternalUpdate()
{ {
// 更新异步请求操作 // 更新异步请求操作
@ -675,6 +670,10 @@ namespace YooAsset
} }
} }
} }
internal static string ConvertAddress(string address)
{
return _bundleServices.ConvertAddress(address);
}
#endregion #endregion
} }
} }