From 092af257d78fc7666628578aa0d1a3b9e2aec865 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=BD=95=E5=86=A0=E5=B3=B0?= <33317070@qq.com>
Date: Wed, 10 Jun 2026 10:36:49 +0800
Subject: [PATCH] feat : optimize package manifest
---
.../Editor/BundleBuilder/BuildBundleInfo.cs | 22 +-
.../BuildTasks/TaskUpdateBundleInfo_IABP.cs | 3 +-
.../BuildTasks/TaskCopyBundledFiles.cs | 16 +-
.../BuildTasks/TaskCreateManifest.cs | 118 ++++----
.../BuildTasks/TaskCreateReport.cs | 68 ++---
.../BuildTasks/TaskUpdateBundleInfo.cs | 3 +-
.../Policies/EvictionByTagsPolicy.cs | 2 +-
.../Runtime/Interfaces/IBundleUnpackPolicy.cs | 34 +--
.../Runtime/ResourcePackage/BuildModel.meta | 8 +
.../ResourcePackage/BuildModel/BuildAsset.cs | 44 +++
.../BuildModel/BuildAsset.cs.meta | 11 +
.../ResourcePackage/BuildModel/BuildBundle.cs | 84 ++++++
.../BuildModel/BuildBundle.cs.meta | 11 +
.../BuildModel/BuildManifest.cs | 259 ++++++++++++++++++
.../BuildModel/BuildManifest.cs.meta | 11 +
.../ResourcePackage/BundleFileNaming.cs | 50 ++++
.../ResourcePackage/BundleFileNaming.cs.meta | 11 +
.../Runtime/ResourcePackage/FileSystemHost.cs | 4 +-
.../Internal/DeserializeManifestOperation.cs | 198 ++++++++++---
.../Internal/SerializeManifestOperation.cs | 153 +++++++++++
.../SerializeManifestOperation.cs.meta | 11 +
.../Runtime/ResourcePackage/PackageAsset.cs | 30 +-
.../Runtime/ResourcePackage/PackageBundle.cs | 92 +------
.../Runtime/ResourcePackage/PackageDetails.cs | 104 +++++--
.../ResourcePackage/PackageManifest.cs | 135 ++-------
.../ResourcePackage/PackageManifestConsts.cs | 6 +-
.../ResourcePackage/PackageManifestHelper.cs | 151 +---------
.../Runtime/ResourcePackage/PackageTags.cs | 86 ++++++
.../ResourcePackage/PackageTags.cs.meta | 11 +
.../YooAsset/Runtime/Utility/BufferReader.cs | 39 +++
.../YooAsset/Runtime/Utility/BufferWriter.cs | 52 ++++
31 files changed, 1244 insertions(+), 583 deletions(-)
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs.meta
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs
create mode 100644 Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildBundleInfo.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildBundleInfo.cs
index a5b09b9d..4e0f54fa 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildBundleInfo.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildBundleInfo.cs
@@ -216,19 +216,19 @@ namespace YooAsset.Editor
}
///
- /// 创建PackageBundle类
+ /// 创建BuildBundle类
///
- internal PackageBundle CreatePackageBundle()
+ internal BuildBundle CreateBuildBundle()
{
- PackageBundle packageBundle = new PackageBundle();
- packageBundle.BundleName = BundleName;
- packageBundle.UnityCrc = PackageUnityCRC;
- packageBundle.FileHash = PackageFileHash;
- packageBundle.FileCrc = PackageFileCRC;
- packageBundle.FileSize = PackageFileSize;
- packageBundle.IsEncrypted = Encrypted;
- packageBundle.DependentBundleIDs = Array.Empty();
- return packageBundle;
+ BuildBundle buildBundle = new BuildBundle();
+ buildBundle.BundleName = BundleName;
+ buildBundle.UnityCrc = PackageUnityCRC;
+ buildBundle.FileHash = PackageFileHash;
+ buildBundle.FileCrc = PackageFileCRC;
+ buildBundle.FileSize = PackageFileSize;
+ buildBundle.IsEncrypted = Encrypted;
+ buildBundle.DependentBundleIDs = Array.Empty();
+ return buildBundle;
}
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildPipeline/InstantAssetBuildPipeline/BuildTasks/TaskUpdateBundleInfo_IABP.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildPipeline/InstantAssetBuildPipeline/BuildTasks/TaskUpdateBundleInfo_IABP.cs
index 00aedac5..e451f5c5 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildPipeline/InstantAssetBuildPipeline/BuildTasks/TaskUpdateBundleInfo_IABP.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildPipeline/InstantAssetBuildPipeline/BuildTasks/TaskUpdateBundleInfo_IABP.cs
@@ -68,8 +68,7 @@ namespace YooAsset.Editor
{
string bundleName = bundleInfo.BundleName;
string fileHash = bundleInfo.PackageFileHash;
- string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(bundleName);
- string fileName = PackageManifestHelper.GetRemoteBundleFileName(outputNameStyle, bundleName, fileExtension, fileHash);
+ string fileName = BundleFileNaming.GetBundleFileName(outputNameStyle, bundleName, fileHash);
bundleInfo.PackageDestFilePath = $"{packageOutputDirectory}/{fileName}";
}
}
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCopyBundledFiles.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCopyBundledFiles.cs
index c93e1e55..a4964ffa 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCopyBundledFiles.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCopyBundledFiles.cs
@@ -25,7 +25,7 @@ namespace YooAsset.Editor
///
/// 拷贝首包资源文件
///
- internal void CopyBundledFilesToStreaming(BuildParametersContext buildParametersContext, PackageManifest manifest)
+ internal void CopyBundledFilesToStreaming(BuildParametersContext buildParametersContext, BuildManifest manifest)
{
EBundledCopyOption copyOption = buildParametersContext.Parameters.BundledCopyOption;
string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
@@ -67,10 +67,10 @@ namespace YooAsset.Editor
// 拷贝文件列表(所有文件)
if (copyOption == EBundledCopyOption.ClearAndCopyAll || copyOption == EBundledCopyOption.OnlyCopyAll)
{
- foreach (var packageBundle in manifest.BundleList)
+ foreach (var buildBundle in manifest.BundleList)
{
- string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
- string destPath = $"{bundledRootDirectory}/{packageBundle.GetFileName()}";
+ string sourcePath = $"{packageOutputDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
+ string destPath = $"{bundledRootDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
EditorFileUtility.CopyFile(sourcePath, destPath, true);
}
}
@@ -85,12 +85,12 @@ namespace YooAsset.Editor
throw new InvalidOperationException(message);
}
string[] tags = copyParams.Split(';');
- foreach (var packageBundle in manifest.BundleList)
+ foreach (var buildBundle in manifest.BundleList)
{
- if (packageBundle.HasAnyTag(tags) == false)
+ if (buildBundle.HasAnyTag(tags) == false)
continue;
- string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
- string destPath = $"{bundledRootDirectory}/{packageBundle.GetFileName()}";
+ string sourcePath = $"{packageOutputDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
+ string destPath = $"{bundledRootDirectory}/{buildBundle.GetFileName(manifest.OutputNameStyle)}";
EditorFileUtility.CopyFile(sourcePath, destPath, true);
}
}
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateManifest.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateManifest.cs
index b4ee6589..240981c5 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateManifest.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateManifest.cs
@@ -12,7 +12,7 @@ namespace YooAsset.Editor
[ContextObject]
public class ManifestContext
{
- internal PackageManifest Manifest;
+ internal BuildManifest Manifest;
}
///
@@ -44,7 +44,7 @@ namespace YooAsset.Editor
CheckBundleHashConflict(buildMapContext);
// 创建新补丁清单
- PackageManifest manifest = new PackageManifest();
+ BuildManifest manifest = new BuildManifest();
manifest.FileVersion = PackageManifestConsts.FileVersion;
manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
manifest.SupportExtensionless = buildMapContext.Command.SupportExtensionless;
@@ -74,20 +74,15 @@ namespace YooAsset.Editor
// 4. 处理首包资源包
if (processBundleDepends)
{
- // 注意:初始化资源清单建立引用关系
- manifest.Initialize();
+ // 注意:建立资源包引用关系图
+ manifest.BuildReferrerGraph();
ProcessBuiltinBundleDependency(context, manifest);
}
-
- // 创建资源清单文本文件
- {
- string fileName = YooAssetConfiguration.GetManifestJsonFileName(buildParameters.PackageName, buildParameters.PackageVersion);
- string filePath = $"{packageOutputDirectory}/{fileName}";
- PackageManifestHelper.SerializeManifestToJson(filePath, manifest);
- BuildLogger.Log($"Create package manifest file: '{filePath}'.");
- }
+ // 5. 构建全局标签表与目录表
+ manifest.BuildTagTable();
+ manifest.BuildDirectoryTable();
// 创建资源清单二进制文件
string packageHash;
@@ -119,8 +114,7 @@ namespace YooAsset.Editor
// 填充上下文
{
ManifestContext manifestContext = new ManifestContext();
- byte[] bytesData = FileUtility.ReadAllBytes(packagePath);
- manifestContext.Manifest = PackageManifestHelper.DeserializeManifestFromBinary(bytesData, buildParameters.ManifestDecryptor);
+ manifestContext.Manifest = manifest;
context.SetContextObject(manifestContext);
}
}
@@ -158,21 +152,21 @@ namespace YooAsset.Editor
///
/// 创建资源对象列表
///
- private List CreatePackageAssetList(BuildMapContext buildMapContext)
+ private List CreatePackageAssetList(BuildMapContext buildMapContext)
{
- List result = new List(1000);
+ List result = new List(1000);
foreach (var bundleInfo in buildMapContext.Collection)
{
var assetInfos = bundleInfo.GetAllManifestAssetInfos();
foreach (var assetInfo in assetInfos)
{
- PackageAsset packageAsset = new PackageAsset();
- packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
- packageAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
- packageAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
- packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
- packageAsset.EditorUserData = assetInfo;
- result.Add(packageAsset);
+ BuildAsset buildAsset = new BuildAsset();
+ buildAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
+ buildAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
+ buildAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
+ buildAsset.Tags = assetInfo.AssetTags.ToArray();
+ buildAsset.EditorUserData = assetInfo;
+ result.Add(buildAsset);
}
}
@@ -184,13 +178,13 @@ namespace YooAsset.Editor
///
/// 创建资源包列表
///
- private List CreatePackageBundleList(BuildMapContext buildMapContext)
+ private List CreatePackageBundleList(BuildMapContext buildMapContext)
{
- List result = new List(1000);
+ List result = new List(1000);
foreach (var bundleInfo in buildMapContext.Collection)
{
- var packageBundle = bundleInfo.CreatePackageBundle();
- result.Add(packageBundle);
+ var buildBundle = bundleInfo.CreateBuildBundle();
+ result.Add(buildBundle);
}
// 按照BundleName排序
@@ -201,7 +195,7 @@ namespace YooAsset.Editor
///
/// 处理资源清单的资源对象列表
///
- private void ProcessPackageAsset(PackageManifest manifest)
+ private void ProcessPackageAsset(BuildManifest manifest)
{
// 注意:优先缓存资源包索引
for (int index = 0; index < manifest.BundleList.Count; index++)
@@ -211,31 +205,31 @@ namespace YooAsset.Editor
}
// 记录资源对象所属的资源包ID
- foreach (var packageAsset in manifest.AssetList)
+ foreach (var buildAsset in manifest.AssetList)
{
- var assetInfo = packageAsset.EditorUserData as BuildAssetInfo;
- packageAsset.BundleID = GetCachedBundleIndexID(assetInfo.BundleName);
+ var assetInfo = buildAsset.EditorUserData as BuildAssetInfo;
+ buildAsset.BundleID = GetCachedBundleIndexID(assetInfo.BundleName);
}
// 记录资源对象依赖的资源包ID集合
// 注意:依赖关系非引擎构建结果里查询!
- foreach (var packageAsset in manifest.AssetList)
+ foreach (var buildAsset in manifest.AssetList)
{
- var mainAssetInfo = packageAsset.EditorUserData as BuildAssetInfo;
- packageAsset.DependentBundleIDs = GetAssetDependBundleIDs(mainAssetInfo);
+ var mainAssetInfo = buildAsset.EditorUserData as BuildAssetInfo;
+ buildAsset.DependentBundleIDs = GetAssetDependBundleIDs(mainAssetInfo);
}
}
///
/// 处理资源包的依赖集合
///
- private void ProcessBundleDepends(BuildContext context, PackageManifest manifest)
+ private void ProcessBundleDepends(BuildContext context, BuildManifest manifest)
{
// 查询引擎生成的资源包依赖关系,然后记录到清单
- foreach (var packageBundle in manifest.BundleList)
+ foreach (var buildBundle in manifest.BundleList)
{
- int mainBundleID = GetCachedBundleIndexID(packageBundle.BundleName);
- string[] dependNames = GetBundleDepends(context, packageBundle.BundleName);
+ int mainBundleID = GetCachedBundleIndexID(buildBundle.BundleName);
+ string[] dependNames = GetBundleDepends(context, buildBundle.BundleName);
List dependIDs = new List(dependNames.Length);
foreach (var dependName in dependNames)
{
@@ -246,29 +240,29 @@ namespace YooAsset.Editor
// 排序并填充数据
dependIDs.Sort();
- packageBundle.DependentBundleIDs = dependIDs.ToArray();
+ buildBundle.DependentBundleIDs = dependIDs.ToArray();
}
}
///
/// 处理资源包的标签集合
///
- private void ProcessBundleTags(PackageManifest manifest)
+ private void ProcessBundleTags(BuildManifest manifest)
{
- foreach (var packageBundle in manifest.BundleList)
+ foreach (var buildBundle in manifest.BundleList)
{
- packageBundle.Tags = Array.Empty();
+ buildBundle.Tags = Array.Empty();
}
// 将主资源的标签信息传染给其依赖的资源包集合
- foreach (var packageAsset in manifest.AssetList)
+ foreach (var buildAsset in manifest.AssetList)
{
- var assetTags = packageAsset.AssetTags;
- int bundleID = packageAsset.BundleID;
+ var assetTags = buildAsset.Tags;
+ int bundleID = buildAsset.BundleID;
CacheBundleTags(bundleID, assetTags);
- if (packageAsset.DependentBundleIDs != null)
+ if (buildAsset.DependentBundleIDs != null)
{
- foreach (var dependBundleID in packageAsset.DependentBundleIDs)
+ foreach (var dependBundleID in buildAsset.DependentBundleIDs)
{
CacheBundleTags(dependBundleID, assetTags);
}
@@ -278,15 +272,15 @@ namespace YooAsset.Editor
// 将缓存的资源标签赋值给资源包
for (int index = 0; index < manifest.BundleList.Count; index++)
{
- var packageBundle = manifest.BundleList[index];
+ var buildBundle = manifest.BundleList[index];
if (_cacheBundleTags.TryGetValue(index, out var value))
{
- packageBundle.Tags = value.ToArray();
+ buildBundle.Tags = value.ToArray();
}
else
{
// 注意:SBP构建管线会自动剔除一些冗余资源的引用关系,导致游离资源包没有被任何主资源包引用。
- string warning = BuildLogger.GetErrorMessage(ErrorCode.FoundStrayBundle, $"Found stray bundle. Bundle ID: {index}, bundle name: '{packageBundle.BundleName}'.");
+ string warning = BuildLogger.GetErrorMessage(ErrorCode.FoundStrayBundle, $"Found stray bundle. Bundle ID: {index}, bundle name: '{buildBundle.BundleName}'.");
BuildLogger.Warning(warning);
}
}
@@ -324,7 +318,7 @@ namespace YooAsset.Editor
}
#region YOOASSET_LEGACY_DEPENDENCY
- private void ProcessBuiltinBundleDependency(BuildContext context, PackageManifest manifest)
+ private void ProcessBuiltinBundleDependency(BuildContext context, BuildManifest manifest)
{
// 注意:如果是可编程构建管线,需要补充首包资源包
// 注意:该步骤依赖前面的操作!
@@ -356,7 +350,7 @@ namespace YooAsset.Editor
}
}
}
- private void ProcessBuiltinBundleReference(PackageManifest manifest, string builtinBundleName)
+ private void ProcessBuiltinBundleReference(BuildManifest manifest, string builtinBundleName)
{
if (string.IsNullOrEmpty(builtinBundleName))
return;
@@ -367,23 +361,23 @@ namespace YooAsset.Editor
// 获取首包资源包
int builtinBundleID = GetCachedBundleIndexID(builtinBundleName);
- var builtinPackageBundle = manifest.BundleList[builtinBundleID];
+ var builtinBuildBundle = manifest.BundleList[builtinBundleID];
// 更新依赖资源包ID集合
- HashSet cacheBundleIDs = new HashSet(builtinPackageBundle.ReferrerBundleIDs);
+ HashSet cacheBundleIDs = new HashSet(builtinBuildBundle.ReferrerBundleIDs);
HashSet tempTags = new HashSet();
- foreach (var packageAsset in manifest.AssetList)
+ foreach (var buildAsset in manifest.AssetList)
{
- if (cacheBundleIDs.Contains(packageAsset.BundleID))
+ if (cacheBundleIDs.Contains(buildAsset.BundleID))
{
- if (packageAsset.DependentBundleIDs.Contains(builtinBundleID) == false)
+ if (buildAsset.DependentBundleIDs.Contains(builtinBundleID) == false)
{
- var tempBundleIDs = new List(packageAsset.DependentBundleIDs);
+ var tempBundleIDs = new List(buildAsset.DependentBundleIDs);
tempBundleIDs.Add(builtinBundleID);
- packageAsset.DependentBundleIDs = tempBundleIDs.ToArray();
+ buildAsset.DependentBundleIDs = tempBundleIDs.ToArray();
}
- foreach (var tag in packageAsset.AssetTags)
+ foreach (var tag in buildAsset.Tags)
{
if (tempTags.Contains(tag) == false)
tempTags.Add(tag);
@@ -392,12 +386,12 @@ namespace YooAsset.Editor
}
// 更新首包资源包的标签集合
- foreach (var tag in builtinPackageBundle.Tags)
+ foreach (var tag in builtinBuildBundle.Tags)
{
if (tempTags.Contains(tag) == false)
tempTags.Add(tag);
}
- builtinPackageBundle.Tags = tempTags.ToArray();
+ builtinBuildBundle.Tags = tempTags.ToArray();
}
private int[] GetAssetDependBundleIDs(BuildAssetInfo mainAssetInfo)
{
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateReport.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateReport.cs
index d32560d4..8482670c 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateReport.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskCreateReport.cs
@@ -22,7 +22,7 @@ namespace YooAsset.Editor
var buildParameters = buildParametersContext.Parameters;
string packageOutputDirectory = buildParametersContext.GetPackageOutputDirectory();
- PackageManifest manifest = manifestContext.Manifest;
+ BuildManifest manifest = manifestContext.Manifest;
BuildReport buildReport = new BuildReport();
// 概述信息
@@ -89,36 +89,36 @@ namespace YooAsset.Editor
// 资源对象列表
buildReport.AssetInfos = new List(manifest.AssetList.Count);
- foreach (var packageAsset in manifest.AssetList)
+ foreach (var buildAsset in manifest.AssetList)
{
- var mainBundle = manifest.BundleList[packageAsset.BundleID];
+ var mainBundle = manifest.BundleList[buildAsset.BundleID];
ReportAssetInfo reportAssetInfo = new ReportAssetInfo();
- reportAssetInfo.Address = packageAsset.Address;
- reportAssetInfo.AssetPath = packageAsset.AssetPath;
- reportAssetInfo.AssetTags = packageAsset.AssetTags;
- reportAssetInfo.AssetGuid = AssetDatabase.AssetPathToGUID(packageAsset.AssetPath);
+ reportAssetInfo.Address = buildAsset.Address;
+ reportAssetInfo.AssetPath = buildAsset.AssetPath;
+ reportAssetInfo.AssetTags = buildAsset.Tags;
+ reportAssetInfo.AssetGuid = AssetDatabase.AssetPathToGUID(buildAsset.AssetPath);
reportAssetInfo.MainBundleName = mainBundle.BundleName;
reportAssetInfo.MainBundleSize = mainBundle.FileSize;
- reportAssetInfo.DependAssets = GetAssetDependAssets(buildMapContext, mainBundle.BundleName, packageAsset.AssetPath);
- reportAssetInfo.DependBundles = GetAssetDependBundles(manifest, packageAsset);
+ reportAssetInfo.DependAssets = GetAssetDependAssets(buildMapContext, mainBundle.BundleName, buildAsset.AssetPath);
+ reportAssetInfo.DependBundles = GetAssetDependBundles(manifest, buildAsset);
buildReport.AssetInfos.Add(reportAssetInfo);
}
// 资源包列表
buildReport.BundleInfos = new List(manifest.BundleList.Count);
- foreach (var packageBundle in manifest.BundleList)
+ foreach (var buildBundle in manifest.BundleList)
{
ReportBundleInfo reportBundleInfo = new ReportBundleInfo();
- reportBundleInfo.BundleName = packageBundle.BundleName;
- reportBundleInfo.FileName = packageBundle.GetFileName();
- reportBundleInfo.FileHash = packageBundle.FileHash;
- reportBundleInfo.FileCrc = packageBundle.FileCrc;
- reportBundleInfo.FileSize = packageBundle.FileSize;
- reportBundleInfo.Encrypted = packageBundle.IsEncrypted;
- reportBundleInfo.Tags = packageBundle.Tags;
- reportBundleInfo.DependBundles = GetBundleDependBundles(manifest, packageBundle);
- reportBundleInfo.ReferenceBundles = GetBundleReferenceBundles(manifest, packageBundle);
- reportBundleInfo.BundleContents = GetBundleContents(buildMapContext, packageBundle.BundleName);
+ reportBundleInfo.BundleName = buildBundle.BundleName;
+ reportBundleInfo.FileName = buildBundle.GetFileName(manifest.OutputNameStyle);
+ reportBundleInfo.FileHash = buildBundle.FileHash;
+ reportBundleInfo.FileCrc = buildBundle.FileCrc;
+ reportBundleInfo.FileSize = buildBundle.FileSize;
+ reportBundleInfo.Encrypted = buildBundle.IsEncrypted;
+ reportBundleInfo.Tags = buildBundle.Tags;
+ reportBundleInfo.DependBundles = GetBundleDependBundles(manifest, buildBundle);
+ reportBundleInfo.ReferenceBundles = GetBundleReferenceBundles(manifest, buildBundle);
+ reportBundleInfo.BundleContents = GetBundleContents(buildMapContext, buildBundle.BundleName);
buildReport.BundleInfos.Add(reportBundleInfo);
}
@@ -151,10 +151,10 @@ namespace YooAsset.Editor
///
/// 获取资源对象依赖的资源包集合
///
- private List GetAssetDependBundles(PackageManifest manifest, PackageAsset packageAsset)
+ private List GetAssetDependBundles(BuildManifest manifest, BuildAsset buildAsset)
{
- List dependBundles = new List(packageAsset.DependentBundleIDs.Length);
- foreach (int index in packageAsset.DependentBundleIDs)
+ List dependBundles = new List(buildAsset.DependentBundleIDs.Length);
+ foreach (int index in buildAsset.DependentBundleIDs)
{
string dependBundleName = manifest.BundleList[index].BundleName;
dependBundles.Add(dependBundleName);
@@ -166,10 +166,10 @@ namespace YooAsset.Editor
///
/// 获取资源包依赖的资源包集合
///
- private List GetBundleDependBundles(PackageManifest manifest, PackageBundle packageBundle)
+ private List GetBundleDependBundles(BuildManifest manifest, BuildBundle buildBundle)
{
- List dependBundles = new List(packageBundle.DependentBundleIDs.Length);
- foreach (int index in packageBundle.DependentBundleIDs)
+ List dependBundles = new List(buildBundle.DependentBundleIDs.Length);
+ foreach (int index in buildBundle.DependentBundleIDs)
{
string dependBundleName = manifest.BundleList[index].BundleName;
dependBundles.Add(dependBundleName);
@@ -181,10 +181,10 @@ namespace YooAsset.Editor
///
/// 获取引用该资源包的资源包集合
///
- private List GetBundleReferenceBundles(PackageManifest manifest, PackageBundle packageBundle)
+ private List GetBundleReferenceBundles(BuildManifest manifest, BuildBundle buildBundle)
{
- List referenceBundles = new List(packageBundle.ReferrerBundleIDs.Count);
- foreach (int index in packageBundle.ReferrerBundleIDs)
+ List referenceBundles = new List(buildBundle.ReferrerBundleIDs.Count);
+ foreach (int index in buildBundle.ReferrerBundleIDs)
{
string dependBundleName = manifest.BundleList[index].BundleName;
referenceBundles.Add(dependBundleName);
@@ -204,15 +204,15 @@ namespace YooAsset.Editor
return result;
}
- private int GetMainAssetCount(PackageManifest manifest)
+ private int GetMainAssetCount(BuildManifest manifest)
{
return manifest.AssetList.Count;
}
- private int GetAllBundleCount(PackageManifest manifest)
+ private int GetAllBundleCount(BuildManifest manifest)
{
return manifest.BundleList.Count;
}
- private long GetAllBundleSize(PackageManifest manifest)
+ private long GetAllBundleSize(BuildManifest manifest)
{
long fileBytes = 0;
foreach (var packageBundle in manifest.BundleList)
@@ -221,7 +221,7 @@ namespace YooAsset.Editor
}
return fileBytes;
}
- private int GetEncryptedBundleCount(PackageManifest manifest)
+ private int GetEncryptedBundleCount(BuildManifest manifest)
{
int fileCount = 0;
foreach (var packageBundle in manifest.BundleList)
@@ -231,7 +231,7 @@ namespace YooAsset.Editor
}
return fileCount;
}
- private long GetEncryptedBundleSize(PackageManifest manifest)
+ private long GetEncryptedBundleSize(BuildManifest manifest)
{
long fileBytes = 0;
foreach (var packageBundle in manifest.BundleList)
diff --git a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskUpdateBundleInfo.cs b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskUpdateBundleInfo.cs
index bb3db8dd..a8ae8d10 100644
--- a/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskUpdateBundleInfo.cs
+++ b/Assets/YooAsset/Editor/BundleBuilder/BuildTasks/TaskUpdateBundleInfo.cs
@@ -61,8 +61,7 @@ namespace YooAsset.Editor
{
string bundleName = bundleInfo.BundleName;
string fileHash = bundleInfo.PackageFileHash;
- string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(bundleName);
- string fileName = PackageManifestHelper.GetRemoteBundleFileName(outputNameStyle, bundleName, fileExtension, fileHash);
+ string fileName = BundleFileNaming.GetBundleFileName(outputNameStyle, bundleName, fileHash);
bundleInfo.PackageDestFilePath = $"{packageOutputDirectory}/{fileName}";
}
}
diff --git a/Assets/YooAsset/Runtime/BundleCache/Policies/EvictionByTagsPolicy.cs b/Assets/YooAsset/Runtime/BundleCache/Policies/EvictionByTagsPolicy.cs
index 3001fbde..e4dbb215 100644
--- a/Assets/YooAsset/Runtime/BundleCache/Policies/EvictionByTagsPolicy.cs
+++ b/Assets/YooAsset/Runtime/BundleCache/Policies/EvictionByTagsPolicy.cs
@@ -33,7 +33,7 @@ namespace YooAsset
{
if (options.Manifest.TryGetPackageBundleByBundleGuid(entry.BundleGuid, out PackageBundle bundle))
{
- if (bundle.HasAnyTag(tags))
+ if (bundle.Tags.HasAnyTag(tags))
bundleGuids.Add(bundle.BundleGuid);
}
}
diff --git a/Assets/YooAsset/Runtime/Interfaces/IBundleUnpackPolicy.cs b/Assets/YooAsset/Runtime/Interfaces/IBundleUnpackPolicy.cs
index 016aa893..7de92637 100644
--- a/Assets/YooAsset/Runtime/Interfaces/IBundleUnpackPolicy.cs
+++ b/Assets/YooAsset/Runtime/Interfaces/IBundleUnpackPolicy.cs
@@ -29,44 +29,14 @@ namespace YooAsset
public bool IsEncrypted => _bundle.IsEncrypted;
///
- /// 分类标签数量
+ /// 分类标签集合
///
- public int TagCount
- {
- get
- {
- return _bundle.Tags == null ? 0 : _bundle.Tags.Length;
- }
- }
+ public PackageTags Tags => _bundle.Tags;
internal BundleUnpackInfo(PackageBundle bundle)
{
_bundle = bundle;
}
-
- ///
- /// 获取指定索引的分类标签
- ///
- public string GetTag(int index)
- {
- return _bundle.Tags[index];
- }
-
- ///
- /// 是否包含指定的单个标签
- ///
- public bool HasTag(string tag)
- {
- return _bundle.HasTag(tag);
- }
-
- ///
- /// 是否包含指定标签数组中的任意一个
- ///
- public bool HasAnyTag(string[] tags)
- {
- return _bundle.HasAnyTag(tags);
- }
}
///
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta
new file mode 100644
index 00000000..acdaed6e
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b097205d69d7e8348968898bc0067ee6
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs
new file mode 100644
index 00000000..b78fbdfc
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs
@@ -0,0 +1,44 @@
+namespace YooAsset
+{
+ ///
+ /// 构建期专用的资源描述
+ ///
+ internal class BuildAsset
+ {
+ ///
+ /// 可寻址地址
+ ///
+ public string Address;
+
+ ///
+ /// 资源路径
+ ///
+ public string AssetPath;
+
+ ///
+ /// 资源GUID
+ ///
+ public string AssetGuid;
+
+ ///
+ /// 所属资源包ID
+ ///
+ public int BundleID;
+
+ ///
+ /// 依赖的资源包ID集合
+ /// 说明:框架层收集查询结果
+ ///
+ public int[] DependentBundleIDs;
+
+ ///
+ /// 资源的分类标签
+ ///
+ public string[] Tags;
+
+ ///
+ /// 临时数据对象
+ ///
+ public object EditorUserData;
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs.meta
new file mode 100644
index 00000000..5aa58b02
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildAsset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ecfb4d29b9f6c534191ca4d6e3de242a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs
new file mode 100644
index 00000000..0f01e12d
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs
@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace YooAsset
+{
+ ///
+ /// 构建期专用的资源包描述
+ ///
+ internal class BuildBundle
+ {
+ ///
+ /// 资源包名称
+ ///
+ public string BundleName;
+
+ ///
+ /// Unity引擎计算的内容校验码
+ ///
+ public uint UnityCrc;
+
+ ///
+ /// 文件哈希值
+ ///
+ public string FileHash;
+
+ ///
+ /// 文件校验码
+ ///
+ public uint FileCrc;
+
+ ///
+ /// 文件大小(字节数)
+ ///
+ public long FileSize;
+
+ ///
+ /// 是否为加密文件
+ ///
+ public bool IsEncrypted;
+
+ ///
+ /// 依赖的资源包ID集合
+ /// 注意:引擎层构建查询结果
+ ///
+ public int[] DependentBundleIDs;
+
+ ///
+ /// 资源包的分类标签
+ ///
+ public string[] Tags;
+
+ ///
+ /// 引用该资源包的资源包ID集合
+ ///
+ public readonly List ReferrerBundleIDs = new List();
+
+
+ ///
+ /// 获取资源包文件名称
+ ///
+ /// 文件名称样式
+ /// 返回根据命名样式生成的远端文件名
+ public string GetFileName(int outputNameStyle)
+ {
+ return BundleFileNaming.GetBundleFileName(outputNameStyle, BundleName, FileHash);
+ }
+
+ ///
+ /// 是否包含指定标签数组中的任意一个
+ ///
+ public bool HasAnyTag(string[] tags)
+ {
+ if (tags == null || tags.Length == 0 || Tags == null || Tags.Length == 0)
+ return false;
+
+ foreach (var tag in tags)
+ {
+ if (Tags.Contains(tag))
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs.meta
new file mode 100644
index 00000000..cb0aaeb6
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildBundle.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9109a09c01fd3d3448f0bfba85d8759f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs
new file mode 100644
index 00000000..95cc4392
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace YooAsset
+{
+ ///
+ /// 构建期专用的清单模型
+ ///
+ internal class BuildManifest
+ {
+ private bool _referrerGraphBuilt;
+
+ // 全局标签表
+ private bool _tagTableBuilt;
+ private string[] _tagTable;
+ private Dictionary _tagToIndex;
+
+ // 全局目录表
+ private bool _directoryTableBuilt;
+ private string[] _directoryTable;
+ private Dictionary _directoryToIndex;
+
+ ///
+ /// 文件版本
+ ///
+ public int FileVersion;
+
+ ///
+ /// 启用可寻址资源定位
+ ///
+ public bool EnableAddressable;
+
+ ///
+ /// 支持无后缀名的资源定位地址
+ ///
+ public bool SupportExtensionless;
+
+ ///
+ /// 资源定位地址大小写不敏感
+ ///
+ public bool LocationToLower;
+
+ ///
+ /// 包含资源GUID数据
+ ///
+ public bool IncludeAssetGuid;
+
+ ///
+ /// 使用可寻址地址代替资源路径
+ ///
+ public bool ReplaceAssetPathWithAddress;
+
+ ///
+ /// 文件名称样式
+ ///
+ public int OutputNameStyle;
+
+ ///
+ /// 构建资源包类型
+ ///
+ public int BuildBundleType;
+
+ ///
+ /// 构建管线名称
+ ///
+ public string BuildPipeline;
+
+ ///
+ /// 资源包裹名称
+ ///
+ public string PackageName;
+
+ ///
+ /// 资源包裹的版本信息
+ ///
+ public string PackageVersion;
+
+ ///
+ /// 资源包裹的备注信息
+ ///
+ public string PackageNote;
+
+ ///
+ /// 资源列表
+ ///
+ public List AssetList = new List();
+
+ ///
+ /// 资源包列表
+ ///
+ public List BundleList = new List();
+
+ ///
+ /// 全局标签表(去重后的标签数组)
+ ///
+ public string[] TagTable
+ {
+ get { return _tagTable; }
+ }
+
+ ///
+ /// 全局目录表(去重后的目录数组)
+ ///
+ public string[] DirectoryTable
+ {
+ get { return _directoryTable; }
+ }
+
+
+ ///
+ /// 构建资源包的引用关系图
+ /// 说明:根据每个资源包的依赖列表,反向填充被依赖方的引用者集合。
+ ///
+ public void BuildReferrerGraph()
+ {
+ if (_referrerGraphBuilt)
+ throw new YooInternalException("BuildReferrerGraph has already been called.");
+ _referrerGraphBuilt = true;
+
+ for (int index = 0; index < BundleList.Count; index++)
+ {
+ var sourceBundle = BundleList[index];
+ if (sourceBundle.DependentBundleIDs == null)
+ continue;
+
+ foreach (int dependIndex in sourceBundle.DependentBundleIDs)
+ {
+ if (dependIndex >= 0 && dependIndex < BundleList.Count)
+ {
+ var dependBundle = BundleList[dependIndex];
+ if (dependBundle.ReferrerBundleIDs.Contains(index))
+ throw new YooInternalException($"Duplicate referrer bundle ID detected: referrer {index} -> bundle {dependIndex}.");
+ dependBundle.ReferrerBundleIDs.Add(index);
+ }
+ else
+ {
+ throw new System.ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {BundleList.Count - 1}.");
+ }
+ }
+ }
+ }
+
+ ///
+ /// 构建去重后的全局标签表,并填充标签到索引的映射
+ ///
+ public void BuildTagTable()
+ {
+ if (_tagTableBuilt)
+ throw new YooInternalException("BuildTagTable has already been called.");
+ _tagTableBuilt = true;
+
+ HashSet tagSet = new HashSet();
+ foreach (var buildAsset in AssetList)
+ {
+ if (buildAsset.Tags == null)
+ continue;
+ foreach (var tag in buildAsset.Tags)
+ {
+ tagSet.Add(tag);
+ }
+ }
+ foreach (var buildBundle in BundleList)
+ {
+ if (buildBundle.Tags == null)
+ continue;
+ foreach (var tag in buildBundle.Tags)
+ {
+ tagSet.Add(tag);
+ }
+ }
+
+ var tagTable = tagSet.ToList();
+ tagTable.Sort(StringComparer.Ordinal); //注意:排序增加序列化稳定性
+
+ if (tagTable.Count > ushort.MaxValue)
+ throw new YooInternalException($"Tag count exceeds the maximum value of {ushort.MaxValue}.");
+
+ _tagTable = tagTable.ToArray();
+ _tagToIndex = new Dictionary(_tagTable.Length);
+ for (int index = 0; index < _tagTable.Length; index++)
+ {
+ _tagToIndex.Add(_tagTable[index], index);
+ }
+ }
+
+ ///
+ /// 将标签字符串数组转换为全局标签表的索引数组
+ ///
+ public ushort[] ConvertTagsToIndices(string[] tags)
+ {
+ if (tags == null || tags.Length == 0)
+ return Array.Empty();
+
+ ushort[] indices = new ushort[tags.Length];
+ for (int i = 0; i < tags.Length; i++)
+ {
+ indices[i] = (ushort)_tagToIndex[tags[i]];
+ }
+ return indices;
+ }
+
+ ///
+ /// 构建去重后的全局目录表,并填充目录到索引的映射
+ ///
+ public void BuildDirectoryTable()
+ {
+ if (_directoryTableBuilt)
+ throw new YooInternalException("BuildDirectoryTable has already been called.");
+ _directoryTableBuilt = true;
+
+ HashSet dirSet = new HashSet();
+ foreach (var buildAsset in AssetList)
+ {
+ SplitAssetPath(buildAsset.AssetPath, out string directory, out string fileName);
+ dirSet.Add(directory);
+ }
+
+ var dirTable = dirSet.ToList();
+ dirTable.Sort(StringComparer.Ordinal); //注意:排序增加序列化稳定性
+
+ if (dirTable.Count > ushort.MaxValue)
+ throw new YooInternalException($"Directory count exceeds the maximum value of {ushort.MaxValue}.");
+
+ _directoryTable = dirTable.ToArray();
+ _directoryToIndex = new Dictionary(_directoryTable.Length);
+ for (int index = 0; index < _directoryTable.Length; index++)
+ {
+ _directoryToIndex.Add(_directoryTable[index], index);
+ }
+ }
+
+ ///
+ /// 获取资源父目录在全局目录表中的索引
+ ///
+ public ushort ConvertDirectoryToIndex(string directory)
+ {
+ return (ushort)_directoryToIndex[directory];
+ }
+
+ ///
+ /// 拆分资源路径为「父目录」与「文件名」
+ ///
+ public static void SplitAssetPath(string assetPath, out string directory, out string fileName)
+ {
+ int slash = assetPath.LastIndexOf('/');
+ if (slash < 0)
+ {
+ directory = string.Empty;
+ fileName = assetPath;
+ }
+ else
+ {
+ directory = assetPath.Substring(0, slash);
+ fileName = assetPath.Substring(slash + 1);
+ }
+ }
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs.meta
new file mode 100644
index 00000000..f2eced19
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BuildModel/BuildManifest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f77081497483ffd44b94a78d4e95203c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs b/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs
new file mode 100644
index 00000000..b80c4dda
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs
@@ -0,0 +1,50 @@
+using System.IO;
+
+namespace YooAsset
+{
+ ///
+ /// 资源包文件命名工具
+ ///
+ internal static class BundleFileNaming
+ {
+ ///
+ /// 获取资源包的文件名
+ ///
+ /// 文件名称样式
+ /// 资源包名称
+ /// 文件哈希值
+ /// 返回根据命名样式生成的远端文件名
+ public static string GetBundleFileName(int nameStyle, string bundleName, string fileHash)
+ {
+ EFileNameStyle style = (EFileNameStyle)nameStyle;
+ switch (style)
+ {
+ case EFileNameStyle.HashName:
+ {
+ string fileExtension = Path.GetExtension(bundleName);
+ return StringUtility.Format("{0}{1}", fileHash, fileExtension);
+ }
+
+ case EFileNameStyle.BundleName:
+ return bundleName;
+
+ case EFileNameStyle.BundleName_HashName:
+ {
+ string fileExtension = Path.GetExtension(bundleName);
+ if (string.IsNullOrEmpty(fileExtension))
+ {
+ return StringUtility.Format("{0}_{1}", bundleName, fileHash);
+ }
+ else
+ {
+ string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
+ return StringUtility.Format("{0}_{1}{2}", fileName, fileHash, fileExtension);
+ }
+ }
+
+ default:
+ throw new System.NotImplementedException($"Invalid name style: {nameStyle}.");
+ }
+ }
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs.meta
new file mode 100644
index 00000000..34cc8c16
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/BundleFileNaming.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8eb74dcfa1fb85948ad1da5f188a6677
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/FileSystemHost.cs b/Assets/YooAsset/Runtime/ResourcePackage/FileSystemHost.cs
index 8424f30e..3a5f711c 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/FileSystemHost.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/FileSystemHost.cs
@@ -421,14 +421,14 @@ namespace YooAsset
if (predicate(fileSystem, packageBundle))
{
// 注意:未标记的资源包视为公共依赖,始终包含在下载列表中
- if (packageBundle.IsTagged() == false)
+ if (packageBundle.Tags.IsTagged == false)
{
var bundleInfo = new BundleInfo(fileSystem, packageBundle);
result.Add(bundleInfo);
}
else
{
- if (packageBundle.HasAnyTag(tags))
+ if (packageBundle.Tags.HasAnyTag(tags))
{
var bundleInfo = new BundleInfo(fileSystem, packageBundle);
result.Add(bundleInfo);
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/DeserializeManifestOperation.cs b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/DeserializeManifestOperation.cs
index dc3617bd..df453a0c 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/DeserializeManifestOperation.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/DeserializeManifestOperation.cs
@@ -13,7 +13,7 @@ namespace YooAsset
private enum ESteps
{
None,
- RestoreFileData,
+ DecryptManifest,
DeserializeFileHeader,
PrepareAssetList,
DeserializeAssetList,
@@ -26,6 +26,8 @@ namespace YooAsset
private readonly IManifestDecryptor _decryptor;
private byte[] _sourceData;
private BufferReader _buffer;
+ private string[] _tagTable;
+ private string[] _directoryTable;
private int _packageAssetCount;
private int _packageBundleCount;
private int _progressTotalValue;
@@ -48,14 +50,14 @@ namespace YooAsset
}
protected override void InternalStart()
{
- _steps = ESteps.RestoreFileData;
+ _steps = ESteps.DecryptManifest;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
- if (_steps == ESteps.RestoreFileData)
+ if (_steps == ESteps.DecryptManifest)
{
if (_decryptor != null)
{
@@ -110,6 +112,12 @@ namespace YooAsset
Manifest.PackageVersion = _buffer.ReadString();
Manifest.PackageNote = _buffer.ReadString();
+ // 读取全局标签表
+ _tagTable = _buffer.ReadStringArray();
+
+ // 读取全局目录表
+ _directoryTable = _buffer.ReadStringArray();
+
// 检测配置
if (Manifest.EnableAddressable && Manifest.LocationToLower)
throw new YooManifestInvalidException("Addressable mode does not support converting locations to lowercase.");
@@ -138,18 +146,35 @@ namespace YooAsset
while (_packageAssetCount > 0)
{
var packageAsset = new PackageAsset();
- packageAsset.Address = _buffer.ReadString();
+
+ // Address
+ if (Manifest.EnableAddressable)
+ packageAsset.Address = _buffer.ReadString();
+ else
+ packageAsset.Address = string.Empty;
+
+ // AssetPath
+ ushort dirIndex = _buffer.ReadUInt16();
if (replaceAssetPath)
{
packageAsset.AssetPath = packageAsset.Address;
- _buffer.SkipString(); //跳过解析AssetPath
+ _buffer.SkipString(); //跳过解析文件名
}
else
{
- packageAsset.AssetPath = _buffer.ReadString();
+ packageAsset.AssetPath = ResolvePath(dirIndex);
}
- packageAsset.AssetGuid = _buffer.ReadString();
- packageAsset.AssetTags = _buffer.ReadStringArray();
+
+ // AssetGuid
+ if (Manifest.IncludeAssetGuid)
+ packageAsset.AssetGuid = _buffer.ReadHash16();
+ else
+ packageAsset.AssetGuid = string.Empty;
+
+ // Tags
+ var tagIndices = _buffer.ReadUInt16Array();
+ packageAsset.Tags = ResolveTags(tagIndices);
+
packageAsset.BundleID = _buffer.ReadInt32();
packageAsset.DependentBundleIDs = _buffer.ReadInt32Array();
FillAssetCollection(Manifest, packageAsset, replaceAssetPath);
@@ -180,12 +205,13 @@ namespace YooAsset
var packageBundle = new PackageBundle();
packageBundle.BundleName = _buffer.ReadString();
packageBundle.UnityCrc = _buffer.ReadUInt32();
- packageBundle.FileHash = _buffer.ReadString();
+ packageBundle.FileHash = _buffer.ReadHash16();
packageBundle.FileCrc = _buffer.ReadUInt32();
packageBundle.FileSize = _buffer.ReadInt64();
packageBundle.IsEncrypted = _buffer.ReadBoolean();
- packageBundle.Tags = _buffer.ReadStringArray();
+ packageBundle.Tags = ResolveTags(_buffer.ReadUInt16Array());
packageBundle.DependentBundleIDs = _buffer.ReadInt32Array();
+ packageBundle.Initialize(Manifest);
FillBundleCollection(Manifest, packageBundle);
_packageBundleCount--;
@@ -202,7 +228,9 @@ namespace YooAsset
if (_steps == ESteps.InitManifest)
{
- Manifest.Initialize();
+ FillBundleMainAssets(Manifest);
+ FillBundleReferrerBundleIDs(Manifest);
+
_steps = ESteps.Done;
SetResult();
}
@@ -212,51 +240,77 @@ namespace YooAsset
ExecuteBatch();
}
+ private PackageTags ResolveTags(ushort[] tagIndices)
+ {
+ if (tagIndices.Length == 0)
+ return new PackageTags(Array.Empty());
+
+ string[] tags = new string[tagIndices.Length];
+ for (int i = 0; i < tagIndices.Length; i++)
+ {
+ ushort tagIndex = tagIndices[i];
+
+#if UNITY_EDITOR || DEBUG
+ if (tagIndex >= _tagTable.Length)
+ throw new YooManifestInvalidException($"Invalid tag index: {tagIndex}. Valid range is 0 to {_tagTable.Length - 1}.");
+#endif
+
+ tags[i] = _tagTable[tagIndex];
+ }
+
+ return new PackageTags(tags);
+ }
+ private string ResolvePath(ushort dirIndex)
+ {
+#if UNITY_EDITOR || DEBUG
+ if (dirIndex >= _directoryTable.Length)
+ throw new YooManifestInvalidException($"Invalid directory index: {dirIndex}. Valid range is 0 to {_directoryTable.Length - 1}.");
+#endif
+
+ string fileName = _buffer.ReadString();
+ string directory = _directoryTable[dirIndex];
+ if (string.IsNullOrEmpty(directory))
+ return fileName;
+ else
+ return string.Concat(directory, "/", fileName);
+ }
+
private void CreateAssetCollection(PackageManifest manifest, int assetCount)
{
manifest.AssetList = new List(assetCount);
- manifest.AssetDictionary = new Dictionary(assetCount);
if (manifest.EnableAddressable)
{
- manifest.AssetPathsByLocation = new Dictionary(assetCount * 3);
+ manifest.AssetsByLocation = new Dictionary(assetCount * 3);
}
else
{
if (manifest.LocationToLower)
- manifest.AssetPathsByLocation = new Dictionary(assetCount * 2, StringComparer.OrdinalIgnoreCase);
+ manifest.AssetsByLocation = new Dictionary(assetCount * 2, StringComparer.OrdinalIgnoreCase);
else
- manifest.AssetPathsByLocation = new Dictionary(assetCount * 2);
+ manifest.AssetsByLocation = new Dictionary(assetCount * 2);
}
if (manifest.IncludeAssetGuid)
- manifest.AssetPathsByGuid = new Dictionary(assetCount);
+ manifest.AssetsByGuid = new Dictionary(assetCount);
else
- manifest.AssetPathsByGuid = new Dictionary();
+ manifest.AssetsByGuid = new Dictionary();
}
private void FillAssetCollection(PackageManifest manifest, PackageAsset packageAsset, bool replaceAssetPath)
{
// 添加到列表集合
manifest.AssetList.Add(packageAsset);
- // 注意:我们不允许原始路径存在重名
- string assetPath = packageAsset.AssetPath;
-#if UNITY_EDITOR || DEBUG
- if (manifest.AssetDictionary.ContainsKey(assetPath))
- throw new YooManifestInvalidException($"Asset path already exists: '{assetPath}'.");
-#endif
- manifest.AssetDictionary.Add(assetPath, packageAsset);
-
- // 填充AssetPathMapping1
+ // 填充AssetsByLocation
{
string location = packageAsset.AssetPath;
- // 添加原生路径的映射
+ // 添加原生路径的映射(注意:我们不允许原始路径存在重名)
#if UNITY_EDITOR || DEBUG
- if (manifest.AssetPathsByLocation.ContainsKey(location))
- throw new YooManifestInvalidException($"Location already exists: '{location}'.");
+ if (manifest.AssetsByLocation.ContainsKey(location))
+ throw new YooManifestInvalidException($"Asset path already exists: '{location}'.");
#endif
- manifest.AssetPathsByLocation.Add(location, packageAsset.AssetPath);
+ manifest.AssetsByLocation.Add(location, packageAsset);
// 添加无后缀名路径的映射
if (manifest.SupportExtensionless)
@@ -264,22 +318,22 @@ namespace YooAsset
string locationWithoutExtension = Path.ChangeExtension(location, null);
if (ReferenceEquals(location, locationWithoutExtension) == false)
{
- if (manifest.AssetPathsByLocation.ContainsKey(locationWithoutExtension))
+ if (manifest.AssetsByLocation.ContainsKey(locationWithoutExtension))
YooLogger.LogWarning($"Location already exists: '{locationWithoutExtension}'.");
else
- manifest.AssetPathsByLocation.Add(locationWithoutExtension, packageAsset.AssetPath);
+ manifest.AssetsByLocation.Add(locationWithoutExtension, packageAsset);
}
}
}
- // 填充AssetPathMapping2
+ // 填充AssetsByGuid
if (manifest.IncludeAssetGuid)
{
#if UNITY_EDITOR || DEBUG
- if (manifest.AssetPathsByGuid.ContainsKey(packageAsset.AssetGuid))
+ if (manifest.AssetsByGuid.ContainsKey(packageAsset.AssetGuid))
throw new YooManifestInvalidException($"Asset GUID already exists: '{packageAsset.AssetGuid}'.");
#endif
- manifest.AssetPathsByGuid.Add(packageAsset.AssetGuid, packageAsset.AssetPath);
+ manifest.AssetsByGuid.Add(packageAsset.AssetGuid, packageAsset);
}
// 添加可寻址地址
@@ -289,10 +343,10 @@ namespace YooAsset
if (string.IsNullOrEmpty(location) == false)
{
#if UNITY_EDITOR || DEBUG
- if (manifest.AssetPathsByLocation.ContainsKey(location))
+ if (manifest.AssetsByLocation.ContainsKey(location))
throw new YooManifestInvalidException($"Location already exists: '{location}'.");
#endif
- manifest.AssetPathsByLocation.Add(location, packageAsset.AssetPath);
+ manifest.AssetsByLocation.Add(location, packageAsset);
}
}
}
@@ -306,9 +360,6 @@ namespace YooAsset
}
private void FillBundleCollection(PackageManifest manifest, PackageBundle packageBundle)
{
- // 初始化资源包
- packageBundle.Initialize(manifest);
-
// 添加到列表集合
manifest.BundleList.Add(packageBundle);
@@ -316,5 +367,72 @@ namespace YooAsset
manifest.BundlesByFileName.Add(packageBundle.GetFileName(), packageBundle);
manifest.BundlesByGuid.Add(packageBundle.BundleGuid, packageBundle);
}
+
+ private void FillBundleMainAssets(PackageManifest manifest)
+ {
+ int bundleCount = manifest.BundleList.Count;
+
+ // 1. 统计每个资源包的主资源数量
+ int[] mainAssetCounts = new int[bundleCount];
+ foreach (var packageAsset in manifest.AssetList)
+ {
+ int bundleID = packageAsset.BundleID;
+ if (bundleID < 0 || bundleID >= bundleCount)
+ throw new ArgumentOutOfRangeException($"Invalid bundle ID: {bundleID}. Valid range is 0 to {bundleCount - 1}.");
+
+ mainAssetCounts[bundleID]++;
+ }
+
+ // 2. 创建列表
+ for (int index = 0; index < bundleCount; index++)
+ {
+ int capacity = mainAssetCounts[index];
+ manifest.BundleList[index].MainAssets = new List(capacity);
+ }
+
+ // 3. 填充数据
+ foreach (var packageAsset in manifest.AssetList)
+ {
+ manifest.BundleList[packageAsset.BundleID].MainAssets.Add(packageAsset);
+ }
+ }
+ private void FillBundleReferrerBundleIDs(PackageManifest manifest)
+ {
+ int bundleCount = manifest.BundleList.Count;
+
+ // 1. 统计每个资源包被引用的次数
+ int[] referrerCounts = new int[bundleCount];
+ for (int index = 0; index < bundleCount; index++)
+ {
+ foreach (int dependIndex in manifest.BundleList[index].DependentBundleIDs)
+ {
+ if (dependIndex < 0 || dependIndex >= bundleCount)
+ throw new ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {bundleCount - 1}.");
+
+ referrerCounts[dependIndex]++;
+ }
+ }
+
+ // 2. 创建列表
+ for (int index = 0; index < bundleCount; index++)
+ {
+ int capacity = referrerCounts[index];
+ manifest.BundleList[index].ReferrerBundleIDs = new List(capacity);
+ }
+
+ // 3. 填充数据
+ for (int index = 0; index < bundleCount; index++)
+ {
+ foreach (int dependIndex in manifest.BundleList[index].DependentBundleIDs)
+ {
+ var dependBundle = manifest.BundleList[dependIndex];
+#if UNITY_EDITOR || DEBUG
+ if (dependBundle.ReferrerBundleIDs.Contains(index))
+ throw new YooManifestInvalidException($"Duplicate referrer bundle ID detected: referrer {index} -> bundle {dependIndex}.");
+#endif
+ dependBundle.ReferrerBundleIDs.Add(index);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs
new file mode 100644
index 00000000..2d276ec7
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs
@@ -0,0 +1,153 @@
+namespace YooAsset
+{
+ ///
+ /// 序列化清单文件操作
+ ///
+ internal class SerializeManifestOperation : AsyncOperationBase
+ {
+ private enum ESteps
+ {
+ None,
+ SerializeFileHeader,
+ SerializeAssetList,
+ SerializeBundleList,
+ EncryptManifest,
+ Done,
+ }
+
+ private readonly BuildManifest _manifest;
+ private readonly IManifestEncryptor _encryptor;
+ private BufferWriter _buffer;
+ private ESteps _steps = ESteps.None;
+
+ ///
+ /// 序列化后的二进制数据
+ ///
+ public byte[] FileData { get; private set; }
+
+ ///
+ /// 创建序列化清单文件操作实例
+ ///
+ /// 清单对象
+ /// 清单加密器,为null时不加密
+ public SerializeManifestOperation(BuildManifest manifest, IManifestEncryptor encryptor)
+ {
+ _manifest = manifest;
+ _encryptor = encryptor;
+ }
+ protected override void InternalStart()
+ {
+ _steps = ESteps.SerializeFileHeader;
+ }
+ protected override void InternalUpdate()
+ {
+ if (_steps == ESteps.None || _steps == ESteps.Done)
+ return;
+
+ if (_steps == ESteps.SerializeFileHeader)
+ {
+ // 创建缓存器
+ _buffer = new BufferWriter(PackageManifestConsts.MaxFileSize);
+
+ // 写入文件标记
+ _buffer.WriteUInt32(PackageManifestConsts.FileMagic);
+
+ // 写入文件版本
+ _buffer.WriteInt32(_manifest.FileVersion);
+
+ // 写入文件头信息
+ _buffer.WriteBoolean(_manifest.EnableAddressable);
+ _buffer.WriteBoolean(_manifest.SupportExtensionless);
+ _buffer.WriteBoolean(_manifest.LocationToLower);
+ _buffer.WriteBoolean(_manifest.IncludeAssetGuid);
+ _buffer.WriteBoolean(_manifest.ReplaceAssetPathWithAddress);
+ _buffer.WriteInt32(_manifest.OutputNameStyle);
+ _buffer.WriteInt32(_manifest.BuildBundleType);
+ _buffer.WriteString(_manifest.BuildPipeline);
+ _buffer.WriteString(_manifest.PackageName);
+ _buffer.WriteString(_manifest.PackageVersion);
+ _buffer.WriteString(_manifest.PackageNote);
+
+ // 写入全局标签表
+ _buffer.WriteStringArray(_manifest.TagTable);
+
+ // 写入全局目录表
+ _buffer.WriteStringArray(_manifest.DirectoryTable);
+
+ _steps = ESteps.SerializeAssetList;
+ }
+
+ if (_steps == ESteps.SerializeAssetList)
+ {
+ _buffer.WriteInt32(_manifest.AssetList.Count);
+ for (int i = 0; i < _manifest.AssetList.Count; i++)
+ {
+ var buildAsset = _manifest.AssetList[i];
+
+ // Address
+ if (_manifest.EnableAddressable)
+ _buffer.WriteString(buildAsset.Address);
+
+ // AssetPath
+ BuildManifest.SplitAssetPath(buildAsset.AssetPath, out string directory, out string fileName);
+ ushort directoryIndex = _manifest.ConvertDirectoryToIndex(directory);
+ _buffer.WriteUInt16(directoryIndex);
+ _buffer.WriteString(fileName);
+
+ // AssetGuid
+ if (_manifest.IncludeAssetGuid)
+ _buffer.WriteHash16(buildAsset.AssetGuid);
+
+ // Tags
+ ushort[] tagIndices = _manifest.ConvertTagsToIndices(buildAsset.Tags);
+ _buffer.WriteUInt16Array(tagIndices);
+
+ _buffer.WriteInt32(buildAsset.BundleID);
+ _buffer.WriteInt32Array(buildAsset.DependentBundleIDs);
+ }
+
+ _steps = ESteps.SerializeBundleList;
+ }
+
+ if (_steps == ESteps.SerializeBundleList)
+ {
+ _buffer.WriteInt32(_manifest.BundleList.Count);
+ for (int i = 0; i < _manifest.BundleList.Count; i++)
+ {
+ var buildBundle = _manifest.BundleList[i];
+ ushort[] tagIndices = _manifest.ConvertTagsToIndices(buildBundle.Tags);
+ _buffer.WriteString(buildBundle.BundleName);
+ _buffer.WriteUInt32(buildBundle.UnityCrc);
+ _buffer.WriteHash16(buildBundle.FileHash);
+ _buffer.WriteUInt32(buildBundle.FileCrc);
+ _buffer.WriteInt64(buildBundle.FileSize);
+ _buffer.WriteBoolean(buildBundle.IsEncrypted);
+ _buffer.WriteUInt16Array(tagIndices);
+ _buffer.WriteInt32Array(buildBundle.DependentBundleIDs);
+ }
+
+ _steps = ESteps.EncryptManifest;
+ }
+
+ if (_steps == ESteps.EncryptManifest)
+ {
+ if (_encryptor != null)
+ {
+ var tempBytes = _buffer.ToArray();
+ FileData = _encryptor.Encrypt(tempBytes);
+ }
+ else
+ {
+ FileData = _buffer.ToArray();
+ }
+
+ _steps = ESteps.Done;
+ SetResult();
+ }
+ }
+ protected override void InternalWaitForCompletion()
+ {
+ ExecuteBatch();
+ }
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs.meta
new file mode 100644
index 00000000..38f050e8
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/Operations/Internal/SerializeManifestOperation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dd920db2febc59c48a34b2899bd5c973
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageAsset.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageAsset.cs
index 8c068543..892fa492 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageAsset.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageAsset.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
namespace YooAsset
{
@@ -24,11 +23,6 @@ namespace YooAsset
///
public string AssetGuid;
- ///
- /// 资源的分类标签
- ///
- public string[] AssetTags;
-
///
/// 所属资源包ID
///
@@ -41,29 +35,9 @@ namespace YooAsset
public int[] DependentBundleIDs;
///
- /// 临时数据对象(仅编辑器有效)
+ /// 资源的分类标签
///
[NonSerialized]
- public object EditorUserData;
-
- ///
- /// 是否包含指定的标签
- ///
- /// 要检查的标签数组
- /// 如果包含任意一个标签返回true,否则返回false。
- public bool HasAnyTag(string[] tags)
- {
- if (tags == null || tags.Length == 0)
- return false;
- if (AssetTags == null || AssetTags.Length == 0)
- return false;
-
- foreach (var tag in tags)
- {
- if (AssetTags.Contains(tag))
- return true;
- }
- return false;
- }
+ public PackageTags Tags;
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageBundle.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageBundle.cs
index 5aa9b613..e307543a 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageBundle.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageBundle.cs
@@ -39,11 +39,6 @@ namespace YooAsset
///
public bool IsEncrypted;
- ///
- /// 资源包的分类标签
- ///
- public string[] Tags;
-
///
/// 依赖的资源包ID集合
/// 注意:引擎层构建查询结果
@@ -59,28 +54,28 @@ namespace YooAsset
get { return FileHash; }
}
+ ///
+ /// 资源包的分类标签
+ ///
+ [NonSerialized]
+ public PackageTags Tags;
+
///
/// 包含的主资源集合
///
[NonSerialized]
- public readonly List MainAssets = new List(10);
+ public List MainAssets;
///
- /// 引用该资源包的资源包列表
+ /// 引用该资源包的资源包ID列表
/// 说明:谁引用了该资源包
///
[NonSerialized]
- public readonly List ReferrerBundleIDs = new List(10);
- [NonSerialized]
- private readonly HashSet _referrerBundleIDs = new HashSet();
+ public List ReferrerBundleIDs;
[NonSerialized]
private PackageManifest _manifest;
[NonSerialized]
- private bool _isInitialized;
- [NonSerialized]
- private int _bundleType;
- [NonSerialized]
private string _fileName;
@@ -94,14 +89,10 @@ namespace YooAsset
///
/// 初始化资源包
///
- /// 所属的资源清单
public void Initialize(PackageManifest manifest)
{
- _isInitialized = true;
_manifest = manifest;
- _bundleType = manifest.BuildBundleType;
- string fileExtension = PackageManifestHelper.GetRemoteBundleFileExtension(BundleName);
- _fileName = PackageManifestHelper.GetRemoteBundleFileName(manifest.OutputNameStyle, BundleName, fileExtension, FileHash);
+ _fileName = BundleFileNaming.GetBundleFileName(manifest.OutputNameStyle, BundleName, FileHash);
}
///
@@ -110,9 +101,9 @@ namespace YooAsset
/// 返回资源包类型
public int GetBundleType()
{
- if (_isInitialized == false)
+ if (_manifest == null)
throw new YooInternalException("PackageBundle is not initialized.");
- return _bundleType;
+ return _manifest.BuildBundleType;
}
///
@@ -121,68 +112,11 @@ namespace YooAsset
/// 返回资源包文件名称
public string GetFileName()
{
- if (_isInitialized == false)
+ if (_manifest == null)
throw new YooInternalException("PackageBundle is not initialized.");
return _fileName;
}
- ///
- /// 添加引用该资源包的资源包ID
- ///
- /// 引用该资源包的资源包ID
- /// 记录谁引用了该资源包
- public void AddReferrerBundleID(int bundleID)
- {
- if (_referrerBundleIDs.Contains(bundleID) == false)
- {
- _referrerBundleIDs.Add(bundleID);
- ReferrerBundleIDs.Add(bundleID);
- }
- }
-
- ///
- /// 是否包含指定的单个标签
- ///
- public bool HasTag(string tag)
- {
- if (Tags == null || Tags.Length == 0)
- return false;
-
- for (int i = 0; i < Tags.Length; i++)
- {
- if (Tags[i] == tag)
- return true;
- }
- return false;
- }
-
- ///
- /// 是否包含指定标签数组中的任意一个
- ///
- public bool HasAnyTag(string[] tags)
- {
- if (tags == null || tags.Length == 0)
- return false;
- if (Tags == null || Tags.Length == 0)
- return false;
-
- for (int i = 0; i < tags.Length; i++)
- {
- if (HasTag(tags[i]))
- return true;
- }
- return false;
- }
-
- ///
- /// 是否包含分类标签
- ///
- /// 如果包含至少一个标签返回true,否则返回false。
- public bool IsTagged()
- {
- return Tags != null && Tags.Length > 0;
- }
-
#region 调试信息
[NonSerialized]
private List _debugReferrerBundleNames;
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageDetails.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageDetails.cs
index 98ef8071..fee498ea 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageDetails.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageDetails.cs
@@ -2,78 +2,150 @@
namespace YooAsset
{
///
- /// 资源包裹的详细信息,用于外部查询包裹配置。
+ /// 资源包裹的详细信息
///
public class PackageDetails
{
+ private readonly PackageManifest _manifest;
+
///
/// 文件版本
///
- public int FileVersion { get; internal set; }
+ public int FileVersion
+ {
+ get { return _manifest.FileVersion; }
+ }
///
/// 启用可寻址资源定位
///
- public bool EnableAddressable { get; internal set; }
+ public bool EnableAddressable
+ {
+ get { return _manifest.EnableAddressable; }
+ }
///
/// 支持无后缀名的资源定位地址
///
- public bool SupportExtensionless { get; internal set; }
+ public bool SupportExtensionless
+ {
+ get { return _manifest.SupportExtensionless; }
+ }
///
/// 资源定位地址大小写不敏感
///
- public bool LocationToLower { get; internal set; }
+ public bool LocationToLower
+ {
+ get { return _manifest.LocationToLower; }
+ }
///
/// 包含资源GUID数据
///
- public bool IncludeAssetGuid { get; internal set; }
+ public bool IncludeAssetGuid
+ {
+ get { return _manifest.IncludeAssetGuid; }
+ }
///
/// 使用可寻址地址代替资源路径
///
- public bool ReplaceAssetPathWithAddress { get; internal set; }
+ public bool ReplaceAssetPathWithAddress
+ {
+ get { return _manifest.ReplaceAssetPathWithAddress; }
+ }
///
/// 文件名称样式
///
- public int OutputNameStyle { get; internal set; }
+ public int OutputNameStyle
+ {
+ get { return _manifest.OutputNameStyle; }
+ }
///
/// 构建资源包类型
///
- public int BuildBundleType { get; internal set; }
+ public int BuildBundleType
+ {
+ get { return _manifest.BuildBundleType; }
+ }
///
/// 构建管线名称
///
- public string BuildPipeline { get; internal set; }
+ public string BuildPipeline
+ {
+ get { return _manifest.BuildPipeline; }
+ }
///
/// 资源包裹名称
///
- public string PackageName { get; internal set; }
+ public string PackageName
+ {
+ get { return _manifest.PackageName; }
+ }
///
/// 资源包裹的版本信息
///
- public string PackageVersion { get; internal set; }
+ public string PackageVersion
+ {
+ get { return _manifest.PackageVersion; }
+ }
///
/// 资源包裹的备注信息
///
- public string PackageNote { get; internal set; }
+ public string PackageNote
+ {
+ get { return _manifest.PackageNote; }
+ }
///
/// 主资源文件总数
///
- public int AssetTotalCount { get; internal set; }
+ public int AssetTotalCount
+ {
+ get { return _manifest.AssetList.Count; }
+ }
///
/// 资源包文件总数
///
- public int BundleTotalCount { get; internal set; }
+ public int BundleTotalCount
+ {
+ get { return _manifest.BundleList.Count; }
+ }
+
+ internal PackageDetails(PackageManifest manifest)
+ {
+ _manifest = manifest;
+ }
+
+ ///
+ /// 返回包裹详细信息的字符串描述
+ ///
+ public override string ToString()
+ {
+ var sb = new System.Text.StringBuilder();
+ sb.AppendLine($"FileVersion : {FileVersion}");
+ sb.AppendLine($"PackageName : {PackageName}");
+ sb.AppendLine($"PackageVersion : {PackageVersion}");
+ sb.AppendLine($"PackageNote : {PackageNote}");
+ sb.AppendLine($"BuildPipeline : {BuildPipeline}");
+ sb.AppendLine($"BuildBundleType : {BuildBundleType}");
+ sb.AppendLine($"OutputNameStyle : {OutputNameStyle}");
+ sb.AppendLine($"EnableAddressable : {EnableAddressable}");
+ sb.AppendLine($"SupportExtensionless : {SupportExtensionless}");
+ sb.AppendLine($"LocationToLower : {LocationToLower}");
+ sb.AppendLine($"IncludeAssetGuid : {IncludeAssetGuid}");
+ sb.AppendLine($"ReplaceAssetPathWithAddress : {ReplaceAssetPathWithAddress}");
+ sb.AppendLine($"AssetTotalCount : {AssetTotalCount}");
+ sb.AppendLine($"BundleTotalCount : {BundleTotalCount}");
+ return sb.ToString();
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifest.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifest.cs
index 1cfa774c..dbb993e2 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifest.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifest.cs
@@ -82,22 +82,16 @@ namespace YooAsset
public List BundleList = new List();
///
- /// 资源映射集合(提供AssetPath获取PackageAsset)
+ /// 资源映射集合(提供Location获取PackageAsset)
///
[NonSerialized]
- public Dictionary AssetDictionary;
+ public Dictionary AssetsByLocation;
///
- /// 资源路径映射集合(提供Location获取AssetPath)
+ /// 资源映射集合(提供AssetGUID获取PackageAsset)
///
[NonSerialized]
- public Dictionary AssetPathsByLocation;
-
- ///
- /// 资源路径映射集合(提供AssetGUID获取AssetPath)
- ///
- [NonSerialized]
- public Dictionary AssetPathsByGuid;
+ public Dictionary AssetsByGuid;
///
/// 资源包集合(提供BundleName获取PackageBundle)
@@ -118,67 +112,13 @@ namespace YooAsset
public Dictionary BundlesByGuid;
- ///
- /// 初始化资源清单
- ///
- public void Initialize()
- {
- // 填充资源包内包含的主资源列表
- foreach (var packageAsset in AssetList)
- {
- int bundleID = packageAsset.BundleID;
- if (bundleID >= 0 && bundleID < BundleList.Count)
- {
- var packageBundle = BundleList[bundleID];
- packageBundle.MainAssets.Add(packageAsset);
- }
- else
- {
- throw new System.ArgumentOutOfRangeException($"Invalid bundle ID: {bundleID}. Valid range is 0 to {BundleList.Count - 1}.");
- }
- }
-
- // 填充资源包引用关系
- for (int index = 0; index < BundleList.Count; index++)
- {
- var sourceBundle = BundleList[index];
- foreach (int dependIndex in sourceBundle.DependentBundleIDs)
- {
- if (dependIndex >= 0 && dependIndex < BundleList.Count)
- {
- var dependBundle = BundleList[dependIndex];
- dependBundle.AddReferrerBundleID(index);
- }
- else
- {
- throw new System.ArgumentOutOfRangeException($"Invalid dependent bundle index: {dependIndex}. Valid range is 0 to {BundleList.Count - 1}.");
- }
- }
- }
- }
-
///
/// 获取包裹的详细信息
///
/// 返回包含包裹配置信息的详细信息对象
public PackageDetails GetPackageDetails()
{
- PackageDetails details = new PackageDetails();
- details.FileVersion = FileVersion;
- details.EnableAddressable = EnableAddressable;
- details.SupportExtensionless = SupportExtensionless;
- details.LocationToLower = LocationToLower;
- details.IncludeAssetGuid = IncludeAssetGuid;
- details.ReplaceAssetPathWithAddress = ReplaceAssetPathWithAddress;
- details.OutputNameStyle = OutputNameStyle;
- details.BuildBundleType = BuildBundleType;
- details.BuildPipeline = BuildPipeline;
- details.PackageName = PackageName;
- details.PackageVersion = PackageVersion;
- details.PackageNote = PackageNote;
- details.AssetTotalCount = AssetList.Count;
- details.BundleTotalCount = BundleList.Count;
- return details;
+ return new PackageDetails(this);
}
///
@@ -191,8 +131,8 @@ namespace YooAsset
if (string.IsNullOrEmpty(location))
return string.Empty;
- if (AssetPathsByLocation.TryGetValue(location, out string assetPath))
- return assetPath;
+ if (AssetsByLocation.TryGetValue(location, out PackageAsset packageAsset))
+ return packageAsset.AssetPath;
else
return string.Empty;
}
@@ -271,7 +211,7 @@ namespace YooAsset
/// 如果找到返回true,否则返回false。
public bool TryGetPackageAsset(string assetPath, out PackageAsset result)
{
- return AssetDictionary.TryGetValue(assetPath, out result);
+ return AssetsByLocation.TryGetValue(assetPath, out result);
}
///
@@ -343,7 +283,7 @@ namespace YooAsset
List result = new List(AssetList.Count);
foreach (var packageAsset in AssetList)
{
- if (packageAsset.HasAnyTag(tags))
+ if (packageAsset.Tags.HasAnyTag(tags))
{
AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, null);
result.Add(assetInfo);
@@ -362,39 +302,20 @@ namespace YooAsset
{
DebugCheckLocation(location);
- string assetPath = ResolveLocationToAssetPath(location);
- if (TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
- {
- AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, assetType);
- return assetInfo;
- }
- else
- {
- string error;
- if (string.IsNullOrEmpty(location))
- error = "Location is null or empty.";
- else
- error = $"Location is invalid: '{location}'.";
- AssetInfo assetInfo = new AssetInfo(PackageName, error);
- return assetInfo;
- }
- }
- private string ResolveLocationToAssetPath(string location)
- {
if (string.IsNullOrEmpty(location))
{
YooLogger.LogError("Failed to map location to asset path. Location is null or empty.");
- return string.Empty;
+ return new AssetInfo(PackageName, "Location is null or empty.");
}
- if (AssetPathsByLocation.TryGetValue(location, out string assetPath))
+ if (AssetsByLocation.TryGetValue(location, out PackageAsset packageAsset))
{
- return assetPath;
+ return new AssetInfo(PackageName, packageAsset, assetType);
}
else
{
YooLogger.LogWarning($"Failed to map location to asset path: '{location}'.");
- return string.Empty;
+ return new AssetInfo(PackageName, $"Location is invalid: '{location}'.");
}
}
@@ -409,43 +330,23 @@ namespace YooAsset
if (IncludeAssetGuid == false)
{
YooLogger.LogWarning("Package manifest does not include asset GUID. Please check asset bundle collector settings.");
- AssetInfo assetInfo = new AssetInfo(PackageName, "AssetGuid data is empty.");
- return assetInfo;
+ return new AssetInfo(PackageName, "AssetGuid data is empty.");
}
- string assetPath = ResolveGuidToAssetPath(assetGuid);
- if (TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
- {
- AssetInfo assetInfo = new AssetInfo(PackageName, packageAsset, assetType);
- return assetInfo;
- }
- else
- {
- string error;
- if (string.IsNullOrEmpty(assetGuid))
- error = "Asset GUID is null or empty.";
- else
- error = $"Asset GUID is invalid: '{assetGuid}'.";
- AssetInfo assetInfo = new AssetInfo(PackageName, error);
- return assetInfo;
- }
- }
- private string ResolveGuidToAssetPath(string assetGuid)
- {
if (string.IsNullOrEmpty(assetGuid))
{
YooLogger.LogError("Failed to map asset GUID to asset path. Asset GUID is null or empty.");
- return string.Empty;
+ return new AssetInfo(PackageName, "Asset GUID is null or empty.");
}
- if (AssetPathsByGuid.TryGetValue(assetGuid, out string assetPath))
+ if (AssetsByGuid.TryGetValue(assetGuid, out PackageAsset packageAsset))
{
- return assetPath;
+ return new AssetInfo(PackageName, packageAsset, assetType);
}
else
{
YooLogger.LogWarning($"Failed to map asset GUID to asset path: '{assetGuid}'.");
- return string.Empty;
+ return new AssetInfo(PackageName, $"Asset GUID is invalid: '{assetGuid}'.");
}
}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestConsts.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestConsts.cs
index 348d6804..568df6a7 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestConsts.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestConsts.cs
@@ -19,11 +19,11 @@ namespace YooAsset
///
/// 文件版本号
///
- public const int FileVersion = 1;
+ public const int FileVersion = 2;
///
- /// 文件最小合法大小(37字节)
+ /// 文件最小合法大小(39字节)
///
- public const int MinFileSize = 37;
+ public const int MinFileSize = 39;
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestHelper.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestHelper.cs
index 93554ce4..41297704 100644
--- a/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestHelper.cs
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageManifestHelper.cs
@@ -39,113 +39,18 @@ namespace YooAsset
return fileHash == hashValue;
}
- ///
- /// 将清单文件序列化为JSON格式
- ///
- /// 保存路径
- /// 清单对象
- public static void SerializeManifestToJson(string savePath, PackageManifest manifest)
- {
- string json = JsonUtility.ToJson(manifest, true);
- FileUtility.WriteAllText(savePath, json);
- }
-
///
/// 将清单文件序列化为二进制格式
///
/// 保存路径
/// 清单对象
/// 清单加密(可为null)
- public static void SerializeManifestToBinary(string savePath, PackageManifest manifest, IManifestEncryptor encryptor)
+ public static void SerializeManifestToBinary(string savePath, BuildManifest manifest, IManifestEncryptor encryptor)
{
- using (FileStream fs = new FileStream(savePath, FileMode.Create))
- {
- // 创建缓存器
- BufferWriter buffer = new BufferWriter(PackageManifestConsts.MaxFileSize);
-
- // 写入文件标记
- buffer.WriteUInt32(PackageManifestConsts.FileMagic);
-
- // 写入文件版本
- buffer.WriteInt32(manifest.FileVersion);
-
- // 写入文件头信息
- buffer.WriteBoolean(manifest.EnableAddressable);
- buffer.WriteBoolean(manifest.SupportExtensionless);
- buffer.WriteBoolean(manifest.LocationToLower);
- buffer.WriteBoolean(manifest.IncludeAssetGuid);
- buffer.WriteBoolean(manifest.ReplaceAssetPathWithAddress);
- buffer.WriteInt32(manifest.OutputNameStyle);
- buffer.WriteInt32(manifest.BuildBundleType);
- buffer.WriteString(manifest.BuildPipeline);
- buffer.WriteString(manifest.PackageName);
- buffer.WriteString(manifest.PackageVersion);
- buffer.WriteString(manifest.PackageNote);
-
- // 写入资源列表
- buffer.WriteInt32(manifest.AssetList.Count);
- for (int i = 0; i < manifest.AssetList.Count; i++)
- {
- var packageAsset = manifest.AssetList[i];
- buffer.WriteString(packageAsset.Address);
- buffer.WriteString(packageAsset.AssetPath);
- buffer.WriteString(packageAsset.AssetGuid);
- buffer.WriteStringArray(packageAsset.AssetTags);
- buffer.WriteInt32(packageAsset.BundleID);
- buffer.WriteInt32Array(packageAsset.DependentBundleIDs);
- }
-
- // 写入资源包列表
- buffer.WriteInt32(manifest.BundleList.Count);
- for (int i = 0; i < manifest.BundleList.Count; i++)
- {
- var packageBundle = manifest.BundleList[i];
- buffer.WriteString(packageBundle.BundleName);
- buffer.WriteUInt32(packageBundle.UnityCrc);
- buffer.WriteString(packageBundle.FileHash);
- buffer.WriteUInt32(packageBundle.FileCrc);
- buffer.WriteInt64(packageBundle.FileSize);
- buffer.WriteBoolean(packageBundle.IsEncrypted);
- buffer.WriteStringArray(packageBundle.Tags);
- buffer.WriteInt32Array(packageBundle.DependentBundleIDs);
- }
-
- // 清单处理操作
- if (encryptor != null)
- {
- var tempBytes = buffer.ToArray();
- var resultBytes = encryptor.Encrypt(tempBytes);
- fs.Write(resultBytes, 0, resultBytes.Length);
- fs.Flush();
- }
- else
- {
- // 写入文件流
- buffer.WriteToStream(fs);
- fs.Flush();
- }
- }
- }
-
- ///
- /// 从JSON字符串反序列化清单文件
- ///
- /// JSON内容字符串
- /// 返回反序列化后的清单对象
- public static PackageManifest DeserializeManifestFromJson(string jsonContent)
- {
- var manifest = JsonUtility.FromJson(jsonContent);
-
- // 初始化资源包
- for (int i = 0; i < manifest.BundleList.Count; i++)
- {
- var packageBundle = manifest.BundleList[i];
- packageBundle.Initialize(manifest);
- }
-
- // 初始化资源清单
- manifest.Initialize();
- return manifest;
+ SerializeManifestOperation operation = new SerializeManifestOperation(manifest, encryptor);
+ operation.StartOperation();
+ operation.WaitForCompletion();
+ FileUtility.WriteAllBytes(savePath, operation.FileData);
}
///
@@ -161,51 +66,5 @@ namespace YooAsset
operation.WaitForCompletion();
return operation.Manifest;
}
-
- ///
- /// 获取资源文件的后缀名
- ///
- /// 资源包名称
- /// 返回文件后缀名(包含点号)
- public static string GetRemoteBundleFileExtension(string bundleName)
- {
- string fileExtension = Path.GetExtension(bundleName);
- return fileExtension;
- }
-
- ///
- /// 获取远端的资源文件名
- ///
- /// 文件名称样式
- /// 资源包名称
- /// 文件后缀名
- /// 文件哈希值
- /// 返回根据命名样式生成的远端文件名
- public static string GetRemoteBundleFileName(int nameStyle, string bundleName, string fileExtension, string fileHash)
- {
- EFileNameStyle style = (EFileNameStyle)nameStyle;
- switch (style)
- {
- case EFileNameStyle.HashName:
- return StringUtility.Format("{0}{1}", fileHash, fileExtension);
-
- case EFileNameStyle.BundleName:
- return bundleName;
-
- case EFileNameStyle.BundleName_HashName:
- if (string.IsNullOrEmpty(fileExtension))
- {
- return StringUtility.Format("{0}_{1}", bundleName, fileHash);
- }
- else
- {
- string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
- return StringUtility.Format("{0}_{1}{2}", fileName, fileHash, fileExtension);
- }
-
- default:
- throw new NotImplementedException($"Invalid name style: {nameStyle}.");
- }
- }
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs b/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs
new file mode 100644
index 00000000..7c8390cf
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs
@@ -0,0 +1,86 @@
+using System;
+
+namespace YooAsset
+{
+ ///
+ /// 标签集合
+ ///
+ public class PackageTags
+ {
+ private readonly string[] _tags;
+
+ ///
+ /// 解析后的完整标签字符串数组
+ ///
+ internal string[] RawTags
+ {
+ get { return _tags; }
+ }
+
+ ///
+ /// 标签数量
+ ///
+ public int TagCount
+ {
+ get { return _tags.Length; }
+ }
+
+ ///
+ /// 是否包含分类标签
+ ///
+ public bool IsTagged
+ {
+ get { return _tags.Length > 0; }
+ }
+
+
+ ///
+ /// 创建标签集合实例
+ ///
+ /// 解析后的标签字符串数组
+ public PackageTags(string[] tags)
+ {
+ if (tags == null)
+ _tags = Array.Empty();
+ else
+ _tags = tags;
+ }
+
+ ///
+ /// 获取指定索引的标签
+ ///
+ public string GetTag(int index)
+ {
+ return _tags[index];
+ }
+
+ ///
+ /// 是否包含指定的单个标签
+ ///
+ public bool HasTag(string tag)
+ {
+ for (int i = 0; i < _tags.Length; i++)
+ {
+ if (_tags[i] == tag)
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// 是否包含指定标签数组中的任意一个
+ ///
+ public bool HasAnyTag(string[] tags)
+ {
+ if (tags == null || tags.Length == 0 || _tags.Length == 0)
+ return false;
+
+ for (int i = 0; i < tags.Length; i++)
+ {
+ if (HasTag(tags[i]))
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta b/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta
new file mode 100644
index 00000000..3c21fcdf
--- /dev/null
+++ b/Assets/YooAsset/Runtime/ResourcePackage/PackageTags.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8d3c7e71f91758244916b4f9d00c24a3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/YooAsset/Runtime/Utility/BufferReader.cs b/Assets/YooAsset/Runtime/Utility/BufferReader.cs
index 3ee587aa..17ceac2d 100644
--- a/Assets/YooAsset/Runtime/Utility/BufferReader.cs
+++ b/Assets/YooAsset/Runtime/Utility/BufferReader.cs
@@ -11,6 +11,7 @@ namespace YooAsset
///
internal class BufferReader
{
+ private readonly char[] _hashChars = new char[32];
private readonly byte[] _buffer;
private int _position = 0;
@@ -163,6 +164,38 @@ namespace YooAsset
return value;
}
+ ///
+ /// 读取哈希值(16字节数据转换为32位哈希值)
+ ///
+ public string ReadHash16()
+ {
+ EnsureCapacity(16);
+ for (int i = 0; i < 16; i++)
+ {
+ byte b = _buffer[_position++];
+ _hashChars[i * 2] = GetHexChar(b >> 4);
+ _hashChars[i * 2 + 1] = GetHexChar(b & 0xF);
+ }
+ return new string(_hashChars);
+ }
+
+ ///
+ /// 读取16位无符号整数数组
+ ///
+ public ushort[] ReadUInt16Array()
+ {
+ ushort count = ReadUInt16();
+ if (count == 0)
+ return Array.Empty();
+
+ ushort[] values = new ushort[count];
+ for (int i = 0; i < count; i++)
+ {
+ values[i] = ReadUInt16();
+ }
+ return values;
+ }
+
///
/// 读取32位有符号整数数组
///
@@ -222,5 +255,11 @@ namespace YooAsset
throw new InvalidOperationException("Insufficient buffer capacity.");
}
}
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static char GetHexChar(int value)
+ {
+ return (char)(value < 10 ? '0' + value : 'a' + value - 10);
+ }
}
}
\ No newline at end of file
diff --git a/Assets/YooAsset/Runtime/Utility/BufferWriter.cs b/Assets/YooAsset/Runtime/Utility/BufferWriter.cs
index b70e7509..eadc139b 100644
--- a/Assets/YooAsset/Runtime/Utility/BufferWriter.cs
+++ b/Assets/YooAsset/Runtime/Utility/BufferWriter.cs
@@ -178,6 +178,47 @@ namespace YooAsset
}
}
+ ///
+ /// 写入哈希值(32位哈希值转换为16字节数据)
+ ///
+ public void WriteHash16(string hashString)
+ {
+ if (hashString == null)
+ throw new ArgumentNullException(nameof(hashString));
+
+#if UNITY_EDITOR || DEBUG
+ if (hashString.Length != 32)
+ throw new InvalidOperationException($"Hash string length must be 32, but got {hashString.Length}: '{hashString}'.");
+#endif
+
+ EnsureCapacity(16);
+ for (int i = 0; i < 16; i++)
+ {
+ int high = GetHexValue(hashString[i * 2]);
+ int low = GetHexValue(hashString[i * 2 + 1]);
+ _buffer[_position++] = (byte)((high << 4) | low);
+ }
+ }
+
+ ///
+ /// 写入16位无符号整数数组
+ ///
+ public void WriteUInt16Array(ushort[] values)
+ {
+ if (values == null)
+ throw new ArgumentNullException(nameof(values));
+
+ int count = values.Length;
+ if (count > ushort.MaxValue)
+ throw new OverflowException($"Array length exceeds the maximum value of {ushort.MaxValue}.");
+
+ WriteUInt16(Convert.ToUInt16(count));
+ for (int i = 0; i < count; i++)
+ {
+ WriteUInt16(values[i]);
+ }
+ }
+
///
/// 写入32位有符号整数数组
///
@@ -243,5 +284,16 @@ namespace YooAsset
throw new InvalidOperationException("Insufficient buffer capacity.");
}
}
+
+ private static int GetHexValue(char c)
+ {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ throw new FormatException($"Invalid hex character: '{c}'.");
+ }
}
}
\ No newline at end of file