feat : Archive file build pipeline

This commit is contained in:
何冠峰
2026-05-11 15:31:46 +08:00
parent 4c717b69db
commit dfa9ff6954
162 changed files with 3101 additions and 375 deletions

View File

@@ -38,7 +38,7 @@ internal class FsmInitializePackage : IStateNode
InitializePackageOperation initializationOperation = null;
if (playMode == EPlayMode.EditorSimulateMode)
{
var buildResult = EditorSimulateBuildInvoker.Build(packageName, (int)EBundleType.VirtualBundle);
var buildResult = EditorSimulateBuildInvoker.Build(packageName, (int)EBundleType.VirtualAssetBundle);
var packageRoot = buildResult.PackageRootDirectory;
var createParameters = new EditorSimulateModeOptions();
createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);

View File

@@ -177,6 +177,46 @@ public static class TestPackageBuilder
throw new System.Exception($"{nameof(RawFileBuildPipeline)} build failed !");
}
}
else if (buildPipelineName == EBuildPipeline.ArchiveFileBuildPipeline.ToString())
{
string projectPath = EditorPathUtility.GetProjectPath();
string outputRoot = $"{projectPath}/Bundles/Tester_AFBP";
var buildParameters = new ArchiveFileBuildParameters();
buildParameters.BuildOutputRoot = outputRoot;
buildParameters.BundledFileRoot = BundleBuilderHelper.GetStreamingAssetsRoot();
buildParameters.BuildPipeline = EBuildPipeline.ArchiveFileBuildPipeline.ToString();
buildParameters.BuildBundleType = (int)EBundleType.ArchiveBundle;
buildParameters.BuildTarget = EditorUserBuildSettings.activeBuildTarget;
buildParameters.PackageName = packageName;
buildParameters.PackageVersion = "TestVersion";
buildParameters.VerifyBuildingResult = true;
buildParameters.FileNameStyle = EFileNameStyle.HashName;
buildParameters.BundledCopyOption = EBundledCopyOption.None;
buildParameters.BundledCopyParams = string.Empty;
buildParameters.ClearBuildCacheFiles = true;
buildParameters.UseAssetDependencyDB = true;
buildParameters.FileAlignment = 4;
var pipeline = new ArchiveFileBuildPipeline();
BuildResult buildResult = pipeline.Run(buildParameters, false);
if (buildResult.Success)
{
string packageRoot = buildResult.OutputPackageDirectory;
bool result = BuiltinCatalogHelper.CreateFile(null, packageName, packageRoot);
if (result == false)
Debug.LogError($"Create package {packageName} catalog file failed ! See the detail error in console !");
var packageResult = new PackageBuildResult();
packageResult.PackageRootDirectory = packageRoot;
return packageResult;
}
else
{
Debug.LogError(buildResult.ErrorInfo);
throw new System.Exception($"{nameof(ArchiveFileBuildPipeline)} build failed !");
}
}
else
{
throw new System.NotImplementedException(buildPipelineName);

View File

@@ -14,10 +14,11 @@ using YooAsset;
/// 测试内容:
/// 1. 销毁 AssetBundleTestPackage 包裹,验证销毁状态,然后移除包裹
/// 2. 根据参数决定是否销毁 RawBundleTestPackage 包裹
/// 3. 根据参数决定是否销毁 ArchiveBundleTestPackage 包裹
/// </remarks>
public class TestDestroyPackage
{
public IEnumerator RuntimeTester(bool destroyRawPackage)
public IEnumerator RuntimeTester(bool destroyRawPackage, bool destroyArchivePackage = false)
{
// 销毁旧资源包 ASSET_BUNDLE
{
@@ -43,5 +44,18 @@ public class TestDestroyPackage
YooAssets.RemovePackage(TestConsts.RawBundlePackageName);
}
// 销毁旧资源包 ARCHIVE_BUNDLE
if (destroyArchivePackage)
{
var package = YooAssets.GetPackage(TestConsts.ArchiveBundlePackageName);
var destroyOp = package.DestroyPackageAsync();
yield return destroyOp;
if (destroyOp.Status != EOperationStatus.Succeeded)
Debug.LogError(destroyOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, destroyOp.Status);
YooAssets.RemovePackage(TestConsts.ArchiveBundlePackageName);
}
}
}

View File

@@ -0,0 +1,107 @@
using System.Collections;
using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using YooAsset;
/// <summary>
/// 测试归档资源包加载
/// </summary>
/// <remarks>
/// 覆盖 API: LoadAssetAsync(RawFileObject) / LoadAssetSync(RawFileObject) / UnloadUnusedAssetsAsync
/// 测试内容:
/// 1. 异步加载归档子文件,验证 GetBytes() 和 GetText() 均返回有效数据raw_file_a
/// 2. 同步加载归档子文件,验证 GetBytes() 和 GetText() 均返回有效数据raw_file_b
/// 3. 重复加载同一归档子文件验证缓存命中不会失败raw_file_c
/// 4. 释放句柄并卸载后重新加载验证卸载保护和重载链路正常raw_file_e
/// </remarks>
public class TestLoadArchiveBundle
{
public IEnumerator RuntimeTester()
{
ResourcePackage package = YooAssets.GetPackage(TestConsts.ArchiveBundlePackageName);
Assert.IsNotNull(package);
// 异步加载归档子文件
{
var assetHandle = package.LoadAssetAsync<RawFileObject>("raw_file_a");
yield return assetHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
assetHandle.Release();
}
// 同步加载归档子文件
{
var assetHandle = package.LoadAssetSync<RawFileObject>("raw_file_b");
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var rawFileObject = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(rawFileObject);
byte[] fileBytes = rawFileObject.GetBytes();
Assert.IsNotNull(fileBytes);
Assert.Greater(fileBytes.Length, 0);
string fileText = rawFileObject.GetText();
Assert.IsNotNull(fileText);
Assert.IsNotEmpty(fileText);
assetHandle.Release();
}
// 重复加载同一归档子文件,验证缓存命中
{
var handle1 = package.LoadAssetAsync<RawFileObject>("raw_file_c");
yield return handle1;
Assert.AreEqual(EOperationStatus.Succeeded, handle1.Status);
var handle2 = package.LoadAssetAsync<RawFileObject>("raw_file_c");
yield return handle2;
Assert.AreEqual(EOperationStatus.Succeeded, handle2.Status);
var obj1 = handle1.GetAssetObject<RawFileObject>();
var obj2 = handle2.GetAssetObject<RawFileObject>();
Assert.IsNotNull(obj1);
Assert.IsNotNull(obj2);
Assert.AreSame(obj1, obj2);
handle1.Release();
handle2.Release();
}
// 释放后卸载再重新加载,验证新旧对象不是同一实例
{
var assetHandle = package.LoadAssetAsync<RawFileObject>("raw_file_e");
yield return assetHandle;
Assert.AreEqual(EOperationStatus.Succeeded, assetHandle.Status);
var previousObj = assetHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(previousObj);
assetHandle.Release();
yield return new WaitForEndOfFrame();
var unloadOp = package.UnloadUnusedAssetsAsync();
yield return unloadOp;
Assert.AreEqual(EOperationStatus.Succeeded, unloadOp.Status);
var reloadHandle = package.LoadAssetAsync<RawFileObject>("raw_file_e");
yield return reloadHandle;
Assert.AreEqual(EOperationStatus.Succeeded, reloadHandle.Status);
var reloadedObj = reloadHandle.GetAssetObject<RawFileObject>();
Assert.IsNotNull(reloadedObj);
Assert.Greater(reloadedObj.GetBytes().Length, 0);
Assert.AreNotSame(previousObj, reloadedObj);
reloadHandle.Release();
}
}
}

View File

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

View File

@@ -23,6 +23,7 @@ public class T0_InitYooAssets : IPrebuildSetup, IPostBuildCleanup
// 创建包裹配置
CreateAssetBundlePackageCollector();
CreateRawBundlePackageCollector();
CreateArchiveBundlePackageCollector();
// 修正配置路径为空导致的错误
YooAsset.Editor.BundleCollectorSettingData.FixFile();
@@ -235,6 +236,23 @@ public class T0_InitYooAssets : IPrebuildSetup, IPostBuildCleanup
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(rawFileGroup, collector1);
}
}
private static void CreateArchiveBundlePackageCollector()
{
var archivePackage = YooAsset.Editor.BundleCollectorSettingData.CreatePackage(TestConsts.ArchiveBundlePackageName);
archivePackage.EnableAddressable = true;
archivePackage.AutoCollectShaders = false;
archivePackage.IgnoreRuleName = "RawFileIgnoreRule";
var archiveFileGroup = YooAsset.Editor.BundleCollectorSettingData.CreateGroup(archivePackage, "ArchiveFileGroup");
{
var collector1 = new YooAsset.Editor.BundleCollector();
collector1.CollectPath = "";
collector1.CollectorGUID = "fddaaf9430e24344196cc82ac3d006b4"; //TestRes/RawFiles目录
collector1.CollectorType = YooAsset.Editor.ECollectorType.MainAssetCollector;
collector1.PackRuleName = nameof(YooAsset.Editor.PackCollector);
YooAsset.Editor.BundleCollectorSettingData.CreateCollector(archiveFileGroup, collector1);
}
}
#endif
[Test]

View File

@@ -18,6 +18,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
{
private const string ASSET_BUNDLE_PACKAGE_ROOT_KEY = "T1_ASSET_BUNDLE_PACKAGE_ROOT_KEY";
private const string RAW_BUNDLE_PACKAGE_ROOT_KEY = "T1_RAW_BUNDLE_PACKAGE_ROOT_KEY";
private const string ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY = "T1_ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY";
void IPrebuildSetup.Setup()
{
@@ -26,7 +27,7 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
{
var simulateParams = new PackageBuildParameters(TestConsts.AssetBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
simulateParams.BuildBundleType = (int)EBundleType.VirtualBundle;
simulateParams.BuildBundleType = (int)EBundleType.VirtualAssetBundle;
simulateParams.AssemblyName = "YooAsset.Tests.Editor";
simulateParams.TypeFullName = "TestPackageBuilder";
simulateParams.MethodName = "BuildPackage";
@@ -34,17 +35,29 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
UnityEditor.EditorPrefs.SetString(ASSET_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建资源包
// 构建原生资源包
{
var simulateParams = new PackageBuildParameters(TestConsts.RawBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
simulateParams.BuildBundleType = (int)EBundleType.RawBundle;
simulateParams.BuildBundleType = (int)EBundleType.VirtualRawBundle;
simulateParams.AssemblyName = "YooAsset.Tests.Editor";
simulateParams.TypeFullName = "TestPackageBuilder";
simulateParams.MethodName = "BuildPackage";
var simulateResult = PackageBuildInvoker.InvokeBuild(simulateParams);
UnityEditor.EditorPrefs.SetString(RAW_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建归档资源包
{
var simulateParams = new PackageBuildParameters(TestConsts.ArchiveBundlePackageName);
simulateParams.BuildPipelineName = "EditorSimulateBuildPipeline";
simulateParams.BuildBundleType = (int)EBundleType.VirtualArchiveBundle;
simulateParams.AssemblyName = "YooAsset.Tests.Editor";
simulateParams.TypeFullName = "TestPackageBuilder";
simulateParams.MethodName = "BuildPackage";
var simulateResult = PackageBuildInvoker.InvokeBuild(simulateParams);
UnityEditor.EditorPrefs.SetString(ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
#endif
}
void IPostBuildCleanup.Cleanup()
@@ -126,6 +139,42 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 ARCHIVE_BUNDLE
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
packageRoot = UnityEditor.EditorPrefs.GetString(ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY);
#endif
if (Directory.Exists(packageRoot) == false)
throw new Exception($"Not found package root : {packageRoot}");
var package = YooAssets.CreatePackage(TestConsts.ArchiveBundlePackageName);
// 初始化资源包
var initParams = new EditorSimulateModeOptions();
initParams.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot);
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
Debug.LogError(initializeOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, initializeOp.Status);
// 请求资源版本
var requestVersionOp = package.RequestPackageVersionAsync();
yield return requestVersionOp;
if (requestVersionOp.Status != EOperationStatus.Succeeded)
Debug.LogError(requestVersionOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, requestVersionOp.Status);
// 更新资源清单
var loadPackageManifestOptions = new LoadPackageManifestOptions(requestVersionOp.PackageVersion, 60);
var loadPackageManifestOp = package.LoadPackageManifestAsync(loadPackageManifestOptions);
yield return loadPackageManifestOp;
if (loadPackageManifestOp.Status != EOperationStatus.Succeeded)
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
}
[UnityTest]
@@ -199,7 +248,14 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator B11_TestUniTask()
public IEnumerator B11_TestLoadArchiveBundle()
{
var tester = new TestLoadArchiveBundle();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator B12_TestUniTask()
{
var tester = new TestUniTask();
yield return tester.RuntimeTester();
@@ -265,6 +321,6 @@ public class T1_TestEditorFileSystem : IPrebuildSetup, IPostBuildCleanup
public IEnumerator Z_DestroyPackage()
{
var tester = new TestDestroyPackage();
yield return tester.RuntimeTester(true);
yield return tester.RuntimeTester(true, true);
}
}

View File

@@ -18,6 +18,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
{
public const string ASSET_BUNDLE_PACKAGE_ROOT_KEY = "T2_ASSET_BUNDLE_PACKAGE_ROOT_KEY";
public const string RAW_BUNDLE_PACKAGE_ROOT_KEY = "T2_RAW_BUNDLE_PACKAGE_ROOT_KEY";
public const string ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY = "T2_ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY";
void IPrebuildSetup.Setup()
{
@@ -43,6 +44,17 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
var simulateResult = PackageBuildInvoker.InvokeBuild(buildParams);
UnityEditor.EditorPrefs.SetString(RAW_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
// 构建ArchiveBundlePackage
{
var buildParams = new PackageBuildParameters(TestConsts.ArchiveBundlePackageName);
buildParams.BuildPipelineName = "ArchiveFileBuildPipeline";
buildParams.AssemblyName = "YooAsset.Tests.Editor";
buildParams.TypeFullName = "TestPackageBuilder";
buildParams.MethodName = "BuildPackage";
var simulateResult = PackageBuildInvoker.InvokeBuild(buildParams);
UnityEditor.EditorPrefs.SetString(ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY, simulateResult.PackageRootDirectory);
}
#endif
}
void IPostBuildCleanup.Cleanup()
@@ -127,6 +139,42 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
// 初始化资源包 ARCHIVE_BUNDLE
{
string packageRoot = string.Empty;
#if UNITY_EDITOR
packageRoot = UnityEditor.EditorPrefs.GetString(ARCHIVE_BUNDLE_PACKAGE_ROOT_KEY);
#endif
if (Directory.Exists(packageRoot) == false)
throw new Exception($"Not found package root : {packageRoot}");
var package = YooAssets.CreatePackage(TestConsts.ArchiveBundlePackageName);
// 初始化资源包
var initParams = new OfflinePlayModeOptions();
initParams.BuiltinFileSystemParameters = FileSystemParameters.CreateDefaultBuiltinFileSystemParameters(packageRoot);
var initializeOp = package.InitializePackageAsync(initParams);
yield return initializeOp;
if (initializeOp.Status != EOperationStatus.Succeeded)
Debug.LogError(initializeOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, initializeOp.Status);
// 请求资源版本
var requestVersionOp = package.RequestPackageVersionAsync();
yield return requestVersionOp;
if (requestVersionOp.Status != EOperationStatus.Succeeded)
Debug.LogError(requestVersionOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, requestVersionOp.Status);
// 更新资源清单
var loadPackageManifestOptions = new LoadPackageManifestOptions(requestVersionOp.PackageVersion, 60);
var loadPackageManifestOp = package.LoadPackageManifestAsync(loadPackageManifestOptions);
yield return loadPackageManifestOp;
if (loadPackageManifestOp.Status != EOperationStatus.Succeeded)
Debug.LogError(loadPackageManifestOp.Error);
Assert.AreEqual(EOperationStatus.Succeeded, loadPackageManifestOp.Status);
}
}
[UnityTest]
@@ -163,7 +211,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
var tester = new TestLoadAllAssets();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator B06_TestLoadGameObject()
{
@@ -200,7 +248,14 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
}
[UnityTest]
public IEnumerator B11_TestUniTask()
public IEnumerator B11_TestLoadArchiveBundle()
{
var tester = new TestLoadArchiveBundle();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator B12_TestUniTask()
{
var tester = new TestUniTask();
yield return tester.RuntimeTester();
@@ -212,7 +267,7 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
var tester = new TestBundleEncryption();
yield return tester.RuntimeTester();
}
[UnityTest]
public IEnumerator C02_TestResourceUnpacker()
{
@@ -295,6 +350,6 @@ public class T2_TestBuiltinFileSystem : IPrebuildSetup, IPostBuildCleanup
public IEnumerator Z_DestroyPackage()
{
var tester = new TestDestroyPackage();
yield return tester.RuntimeTester(true);
yield return tester.RuntimeTester(true, true);
}
}

View File

@@ -14,6 +14,11 @@ public class TestConsts
/// </summary>
public const string RawBundlePackageName = "RawBundleTestPackage";
/// <summary>
/// ArchiveBundle 测试包裹名称
/// </summary>
public const string ArchiveBundlePackageName = "ArchiveBundleTestPackage";
/// <summary>
/// sound.wav 资源的 GUID (TestRes/Audio/sound.wav)
/// </summary>