refactor : 代码重构

This commit is contained in:
何冠峰
2026-01-12 11:09:27 +08:00
committed by 何冠峰
parent d228e41df7
commit 5b81269090
1614 changed files with 44418 additions and 42154 deletions

View File

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

View File

@@ -0,0 +1,523 @@
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 内置文件系统管理内置StreamingAssets目录下的资源文件访问。
/// </summary>
internal class BuiltinFileSystem : IFileSystem
{
/// <summary>
/// 内置资源文件路径映射表
/// </summary>
protected readonly Dictionary<string, string> _builtinFilePathMapping = new Dictionary<string, string>(10000);
/// <summary>
/// 临时文件路径映射表
/// </summary>
protected readonly Dictionary<string, string> _tempFilePathMapping = new Dictionary<string, string>(10000);
/// <summary>
/// 包裹根目录路径
/// </summary>
protected string _packageRoot;
/// <summary>
/// 临时文件根目录路径
/// </summary>
protected string _tempFilesRoot;
/// <summary>
/// 解压清单文件根目录路径
/// </summary>
protected string _unpackManifestFilesRoot;
/// <summary>
/// 解压资源包文件根目录路径
/// </summary>
protected string _unpackBundleFilesRoot;
/// <summary>
/// 内置 Bundle 缓存系统
/// </summary>
public IBundleCache BuiltinBundleCache { get; private set; }
/// <summary>
/// 解压 Bundle 缓存系统
/// </summary>
public IBundleCache UnpackBundleCache { get; private set; }
/// <summary>
/// 解压调度器
/// </summary>
public DownloadSchedulerOperation UnpackScheduler { get; internal set; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; private set; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { get; private set; }
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { get; private set; }
/// <summary>
/// 自定义参数:覆盖安装缓存清理模式
/// </summary>
public EInstallCleanupMode InstallCleanupMode { get; private set; } = EInstallCleanupMode.None;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验级别
/// </summary>
public EFileVerifyLevel FileVerifyLevel { get; private set; } = EFileVerifyLevel.Low;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验最大并发数
/// </summary>
/// <remarks>
/// 默认值8推荐值为处理器数两倍。过大的值可能导致线程池任务过多影响系统稳定性。
/// </remarks>
public int FileVerifyMaxConcurrency { get; private set; } = 8;
/// <summary>
/// 自定义参数:拷贝内置清单
/// </summary>
public bool CopyBuiltinPackageManifest { get; private set; } = false;
/// <summary>
/// 自定义参数:拷贝内置清单的目标目录
/// </summary>
/// <remarks>
/// 注意:该参数为空的时候,会获取默认的沙盒目录。
/// </remarks>
public string CopyBuiltinPackageManifestDestRoot { get; private set; }
/// <summary>
/// 自定义参数:解压文件系统的根目录
/// </summary>
public string UnpackFileSystemRoot { get; private set; }
/// <summary>
/// 自定义参数:最大并发连接数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32
/// </remarks>
public int UnpackMaxConcurrency { get; private set; } = 8;
/// <summary>
/// 自定义参数:每帧发起的最大请求数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32。避免单帧发起过多请求导致卡顿。
/// </remarks>
public int UnpackMaxRequestsPerFrame { get; private set; } = 8;
/// <summary>
/// 自定义参数AssetBundle 解密器
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数RawBundle 解密器
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 备用解密器
/// </summary>
public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; private set; }
/// <summary>
/// 自定义参数:资源清单解密器
/// </summary>
public IManifestDecryptor ManifestDecryptor { get; private set; }
#endregion
/// <summary>
/// 创建实例
/// </summary>
public BuiltinFileSystem()
{
}
/// <inheritdoc />
public FSInitializeOperation InitializeAsync()
{
var operation = new BFSInitializeOperation(this);
return operation;
}
/// <inheritdoc />
public FSRequestPackageVersionOperation RequestPackageVersionAsync(FSRequestPackageVersionOptions options)
{
var operation = new BFSRequestPackageVersionOperation(this);
return operation;
}
/// <inheritdoc />
public FSLoadPackageManifestOperation LoadPackageManifestAsync(FSLoadPackageManifestOptions options)
{
var operation = new BFSLoadPackageManifestOperation(this, options.PackageVersion);
return operation;
}
/// <inheritdoc />
public FSLoadPackageBundleOperation LoadPackageBundleAsync(FSLoadPackageBundleOptions options)
{
var operation = new BFSLoadPackageBundleOperation(this, options);
return operation;
}
/// <inheritdoc />
public FSDownloadBundleOperation DownloadBundleAsync(FSDownloadBundleOptions options)
{
var operation = new BFSDownloadBundleOperation(this, options);
return operation;
}
/// <inheritdoc />
public FSClearCacheOperation ClearCacheAsync(FSClearCacheOptions options)
{
if (options.ClearMethod == ClearCacheMethods.ClearAllManifestFiles)
{
var operation = new FSClearCacheCompleteOperation();
return operation;
}
else if (options.ClearMethod == ClearCacheMethods.ClearUnusedManifestFiles)
{
var operation = new FSClearCacheCompleteOperation();
return operation;
}
else
{
var operation = new BFSClearCacheOperation(this, options);
return operation;
}
}
/// <inheritdoc />
public void SetParameter(string paramName, object value)
{
if (paramName == nameof(EFileSystemParameter.DownloadBackend))
{
DownloadBackend = FileSystemHelper.CastParameter<IDownloadBackend>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.UnityWebRequestCreator))
{
WebRequestCreator = FileSystemHelper.CastParameter<UnityWebRequestCreator>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.InstallCleanupMode))
{
InstallCleanupMode = FileSystemHelper.CastParameter<EInstallCleanupMode>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.FileVerifyLevel))
{
FileVerifyLevel = FileSystemHelper.CastParameter<EFileVerifyLevel>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.FileVerifyMaxConcurrency))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"FILE_VERIFY_MAX_CONCURRENCY value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
FileVerifyMaxConcurrency = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.CopyBuiltinPackageManifest))
{
CopyBuiltinPackageManifest = FileSystemHelper.CastParameter<bool>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.CopyBuiltinPackageManifestDestRoot))
{
CopyBuiltinPackageManifestDestRoot = FileSystemHelper.CastParameter<string>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.UnpackFileSystemRoot))
{
UnpackFileSystemRoot = FileSystemHelper.CastParameter<string>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxConcurrency))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_CONCURRENCY value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
UnpackMaxConcurrency = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxRequestPerFrame))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_REQUEST_PER_FRAME value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
UnpackMaxRequestsPerFrame = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleFallbackDecryptor))
{
AssetBundleFallbackDecryptor = FileSystemHelper.CastParameter<IBundleMemoryDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.ManifestDecryptor))
{
ManifestDecryptor = FileSystemHelper.CastParameter<IManifestDecryptor>(paramName, value);
}
else
{
throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName));
}
}
/// <inheritdoc />
public void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
_packageRoot = GetDefaultBuiltinPackageRoot(packageName);
else
_packageRoot = packageRoot;
// 设置根目录
string unpackRoot;
if (string.IsNullOrEmpty(UnpackFileSystemRoot))
unpackRoot = GetDefaultUnpackPackageRoot(packageName);
else
unpackRoot = UnpackFileSystemRoot;
_unpackManifestFilesRoot = PathUtility.Combine(unpackRoot, BuiltinFileSystemConsts.UnpackManifestFilesFolderName);
_unpackBundleFilesRoot = PathUtility.Combine(unpackRoot, BuiltinFileSystemConsts.UnpackBundleFilesFolderName);
_tempFilesRoot = PathUtility.Combine(unpackRoot, BuiltinFileSystemConsts.UnpackTempFilesFolderName);
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
// 创建内置文件缓存系统
{
var cacheConfig = new BuiltinBundleCache.Configuration(
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
downloadBackend: DownloadBackend);
BuiltinBundleCache = new BuiltinBundleCache(packageName, _packageRoot, cacheConfig);
}
// 创建沙盒文件缓存系统
{
var cacheConfig = new SandboxBundleCache.Configuration(
fileVerifyMaxConcurrency: FileVerifyMaxConcurrency,
fileVerifyLevel: FileVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
assetBundleFallbackDecryptor: AssetBundleFallbackDecryptor);
UnpackBundleCache = new SandboxBundleCache(packageName, _unpackBundleFilesRoot, cacheConfig);
}
}
/// <inheritdoc />
public void OnDestroy()
{
if (BuiltinBundleCache != null)
{
BuiltinBundleCache.Dispose();
BuiltinBundleCache = null;
}
if (UnpackBundleCache != null)
{
UnpackBundleCache.Dispose();
UnpackBundleCache = null;
}
if (UnpackScheduler != null)
{
UnpackScheduler.AbortOperation();
UnpackScheduler = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
/// <inheritdoc />
public bool CanAcceptBundle(PackageBundle bundle)
{
return BuiltinBundleCache.IsCached(bundle.BundleGuid);
}
/// <inheritdoc />
public bool IsDownloadRequired(PackageBundle bundle)
{
return false;
}
/// <inheritdoc />
public bool IsUnpackRequired(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
{
return UnpackBundleCache.IsCached(bundle.BundleGuid) == false;
}
else
{
return false;
}
}
/// <inheritdoc />
public bool IsImportRequired(PackageBundle bundle)
{
return false;
}
/// <summary>
/// 是否属于解压资源包文件
/// </summary>
public bool IsUnpackBundleFile(PackageBundle bundle)
{
if (CanAcceptBundle(bundle) == false)
return false;
#if UNITY_ANDROID || UNITY_OPENHARMONY
if (bundle.IsEncrypted)
return true;
if (bundle.GetBundleType() == (int)EBundleType.RawBundle)
return true;
return false;
#else
return false;
#endif
}
#region
/// <summary>
/// 获取默认的内置包裹根目录
/// </summary>
public string GetDefaultBuiltinPackageRoot(string packageName)
{
string rootDirectory = YooAssetConfiguration.GetDefaultBuiltinRoot();
return PathUtility.Combine(rootDirectory, packageName);
}
/// <summary>
/// 获取内置文件路径
/// </summary>
public string GetBuiltinBundleFilePath(PackageBundle bundle)
{
if (_builtinFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
{
filePath = PathUtility.Combine(_packageRoot, bundle.GetFileName());
_builtinFilePathMapping.Add(bundle.BundleGuid, filePath);
}
return filePath;
}
/// <summary>
/// 获取内置包裹版本文件路径
/// </summary>
public string GetBuiltinPackageVersionFilePath()
{
string fileName = YooAssetConfiguration.GetPackageVersionFileName(PackageName);
return PathUtility.Combine(_packageRoot, fileName);
}
/// <summary>
/// 获取内置包裹哈希文件路径
/// </summary>
public string GetBuiltinPackageHashFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
/// <summary>
/// 获取内置包裹清单文件路径
/// </summary>
public string GetBuiltinPackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
/// <summary>
/// 获取沙盒应用程序水印文件路径
/// </summary>
public string GetSandboxAppFootprintFilePath()
{
return PathUtility.Combine(_unpackManifestFilesRoot, SandboxFileSystemConsts.AppFootprintFileName);
}
/// <summary>
/// 删除所有缓存的资源文件
/// </summary>
public void DeleteAllBundleFiles()
{
if (Directory.Exists(_unpackBundleFilesRoot))
{
Directory.Delete(_unpackBundleFilesRoot, true);
}
}
/// <summary>
/// 删除所有缓存的清单文件
/// </summary>
public void DeleteAllManifestFiles()
{
if (Directory.Exists(_unpackManifestFilesRoot))
{
Directory.Delete(_unpackManifestFilesRoot, true);
}
}
/// <summary>
/// 删除所有缓存的临时文件
/// </summary>
public void DeleteAllTempFiles()
{
if (Directory.Exists(_tempFilesRoot))
{
Directory.Delete(_tempFilesRoot, true);
}
}
/// <summary>
/// 获取默认的解压根目录
/// </summary>
public string GetDefaultUnpackPackageRoot(string packageName)
{
string rootDirectory = YooAssetConfiguration.GetDefaultCacheRoot();
return PathUtility.Combine(rootDirectory, packageName);
}
/// <summary>
/// 获取解压的临时文件路径
/// </summary>
public string GetUnpackTempFilePath(PackageBundle bundle)
{
if (_tempFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
{
filePath = PathUtility.Combine(_tempFilesRoot, bundle.BundleGuid);
_tempFilePathMapping.Add(bundle.BundleGuid, filePath);
}
return filePath;
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,24 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统常量定义
/// </summary>
internal static class BuiltinFileSystemConsts
{
/// <summary>
/// 解压清单文件的文件夹名称
/// </summary>
public const string UnpackManifestFilesFolderName = "UnpackManifestFiles";
/// <summary>
/// 解压资源文件的文件夹名称
/// </summary>
public const string UnpackBundleFilesFolderName = "UnpackBundleFiles";
/// <summary>
/// 解压临时文件的文件夹名称
/// </summary>
public const string UnpackTempFilesFolderName = "UnpackTempFiles";
}
}

View File

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

View File

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

View File

@@ -0,0 +1,74 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的清理缓存操作
/// </summary>
internal sealed class BFSClearCacheOperation : FSClearCacheOperation
{
private enum ESteps
{
None,
CheckReadOnly,
ClearCache,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly FSClearCacheOptions _options;
private BCClearCacheOperation _clearCacheOp;
private ESteps _steps = ESteps.None;
internal BFSClearCacheOperation(BuiltinFileSystem fileSystem, FSClearCacheOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.CheckReadOnly;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckReadOnly)
{
if (_fileSystem.UnpackBundleCache.IsReadOnly)
{
_steps = ESteps.Done;
SetResult();
return;
}
_steps = ESteps.ClearCache;
}
if (_steps == ESteps.ClearCache)
{
if (_clearCacheOp == null)
{
_clearCacheOp = _fileSystem.UnpackBundleCache.ClearCacheAsync(_options.ConvertTo());
_clearCacheOp.StartOperation();
AddChildOperation(_clearCacheOp);
}
_clearCacheOp.UpdateOperation();
if (_clearCacheOp.IsDone == false)
return;
if (_clearCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_clearCacheOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,108 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的解压文件操作
/// </summary>
internal sealed class BFSDownloadBundleOperation : FSDownloadBundleOperation
{
private enum ESteps
{
None,
CheckExists,
CreateUnpack,
CheckUnpack,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly FSDownloadBundleOptions _options;
private DownloadFileBaseOperation _downloadFileOp;
private ESteps _steps = ESteps.None;
internal BFSDownloadBundleOperation(BuiltinFileSystem fileSystem, FSDownloadBundleOptions options) : base(options.Bundle)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.CheckExists;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.UnpackBundleCache.IsCached(Bundle.BundleGuid))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.CreateUnpack;
}
}
// 创建解压器
if (_steps == ESteps.CreateUnpack)
{
_downloadFileOp = _fileSystem.UnpackScheduler.TryGetDownloadOperation(Bundle);
if (_downloadFileOp == null)
{
string builtinFilePath = _fileSystem.GetBuiltinBundleFilePath(Bundle);
_downloadFileOp = new UnpackAndCacheFileOperation(_fileSystem, Bundle, builtinFilePath);
_fileSystem.UnpackScheduler.RegisterDownloadOperation(_downloadFileOp);
}
_steps = ESteps.CheckUnpack;
}
// 检测结果
if (_steps == ESteps.CheckUnpack)
{
if (IsWaitForCompletion)
_downloadFileOp.WaitForCompletion();
// 注意:不主动调用 _downloadFileOp.UpdateOperation()
// 说明:解压任务由 UnpackScheduler 统一驱动,此处仅读取状态。
// 说明:同步等待由 WaitForCompletion() 内部的 ExecuteBatch() 保证。
Progress = _downloadFileOp.Progress;
Report = _downloadFileOp.LatestReport;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
protected override void InternalAbort()
{
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_downloadFileOp != null)
{
_downloadFileOp.Release();
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,192 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的初始化操作
/// </summary>
internal sealed class BFSInitializeOperation : FSInitializeOperation
{
private enum ESteps
{
None,
CheckPlatform,
CheckAppFootprint,
CopyPackageManifest,
InitializeBuiltinBundleCache,
InitializeUnpackBundleCache,
CreateScheduler,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private BCInitializeOperation _initializeBuiltinBundleCacheOp;
private BCInitializeOperation _initializeUnpackBundleCacheOp;
private CopyBuiltinPackageManifestOperation _copyBuiltinPackageManifestOp;
private ESteps _steps = ESteps.None;
internal BFSInitializeOperation(BuiltinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.CheckPlatform;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckPlatform)
{
#if UNITY_WEBGL
_steps = ESteps.Done;
SetError($"{nameof(BuiltinFileSystem)} does not support the WebGL platform.");
#else
_steps = ESteps.CheckAppFootprint;
#endif
}
if (_steps == ESteps.CheckAppFootprint)
{
string footprintFilePath = _fileSystem.GetSandboxAppFootprintFilePath();
var appFootprint = new ApplicationFootprint(footprintFilePath);
appFootprint.Load(_fileSystem.PackageName);
// 如果水印发生变化,则说明覆盖安装后首次打开游戏
if (appFootprint.IsDirty())
{
if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.None)
{
YooLogger.LogWarning("No action required on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllCacheFiles)
{
_fileSystem.DeleteAllBundleFiles();
_fileSystem.DeleteAllManifestFiles();
_fileSystem.DeleteAllTempFiles();
YooLogger.LogWarning("Deleted all cache files on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllBundleFiles)
{
_fileSystem.DeleteAllBundleFiles();
YooLogger.LogWarning("Deleted all bundle files on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllManifestFiles)
{
_fileSystem.DeleteAllManifestFiles();
YooLogger.LogWarning("Deleted all manifest files on overwrite installation.");
}
else
{
throw new YooInternalException($"Unhandled {nameof(EInstallCleanupMode)} value: {_fileSystem.InstallCleanupMode}.");
}
appFootprint.Overwrite(_fileSystem.PackageName);
}
_steps = ESteps.CopyPackageManifest;
}
if (_steps == ESteps.CopyPackageManifest)
{
if (_fileSystem.CopyBuiltinPackageManifest)
{
if (_copyBuiltinPackageManifestOp == null)
{
_copyBuiltinPackageManifestOp = new CopyBuiltinPackageManifestOperation(_fileSystem);
_copyBuiltinPackageManifestOp.StartOperation();
AddChildOperation(_copyBuiltinPackageManifestOp);
}
_copyBuiltinPackageManifestOp.UpdateOperation();
if (_copyBuiltinPackageManifestOp.IsDone == false)
return;
if (_copyBuiltinPackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.InitializeBuiltinBundleCache;
}
else
{
_steps = ESteps.Done;
SetError(_copyBuiltinPackageManifestOp.Error);
}
}
else
{
_steps = ESteps.InitializeBuiltinBundleCache;
}
}
if (_steps == ESteps.InitializeBuiltinBundleCache)
{
if (_initializeBuiltinBundleCacheOp == null)
{
_initializeBuiltinBundleCacheOp = _fileSystem.BuiltinBundleCache.InitializeAsync();
_initializeBuiltinBundleCacheOp.StartOperation();
AddChildOperation(_initializeBuiltinBundleCacheOp);
}
_initializeBuiltinBundleCacheOp.UpdateOperation();
Progress = _initializeBuiltinBundleCacheOp.Progress;
if (_initializeBuiltinBundleCacheOp.IsDone == false)
return;
if (_initializeBuiltinBundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.InitializeUnpackBundleCache;
}
else
{
_steps = ESteps.Done;
SetError(_initializeBuiltinBundleCacheOp.Error);
}
}
if (_steps == ESteps.InitializeUnpackBundleCache)
{
if (_initializeUnpackBundleCacheOp == null)
{
_initializeUnpackBundleCacheOp = _fileSystem.UnpackBundleCache.InitializeAsync();
_initializeUnpackBundleCacheOp.StartOperation();
AddChildOperation(_initializeUnpackBundleCacheOp);
}
_initializeUnpackBundleCacheOp.UpdateOperation();
Progress = _initializeUnpackBundleCacheOp.Progress;
if (_initializeUnpackBundleCacheOp.IsDone == false)
return;
if (_initializeUnpackBundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CreateScheduler;
}
else
{
_steps = ESteps.Done;
SetError(_initializeUnpackBundleCacheOp.Error);
}
}
if (_steps == ESteps.CreateScheduler)
{
// 注意: 下载调度中心在最后一步创建,防止初始化失败后残留任务。
// 注意: 下载调度中心作为独立任务运行!
if (_fileSystem.UnpackScheduler == null)
{
var schedulerConfig = new DownloadSchedulerOperation.Configuration(
schedulerName: _fileSystem.GetType().Name,
downloadBackend: _fileSystem.DownloadBackend,
maxConcurrency: _fileSystem.UnpackMaxConcurrency,
maxRequestsPerFrame: _fileSystem.UnpackMaxRequestsPerFrame);
_fileSystem.UnpackScheduler = new DownloadSchedulerOperation(schedulerConfig);
AsyncOperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.UnpackScheduler);
}
_steps = ESteps.Done;
SetResult();
}
}
}
}

View File

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

View File

@@ -0,0 +1,165 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的加载资源包操作
/// </summary>
internal sealed class BFSLoadPackageBundleOperation : FSLoadPackageBundleOperation
{
private enum ESteps
{
None,
Prepare,
UnpackFile,
AbortUnpack,
LoadUnpackBundle,
LoadBundle,
CheckResult,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly FSLoadPackageBundleOptions _options;
private FSDownloadBundleOperation _unpackFileOp;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal BFSLoadPackageBundleOperation(BuiltinFileSystem fileSystem, FSLoadPackageBundleOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.Prepare;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
if (_fileSystem.IsUnpackBundleFile(_options.Bundle))
{
if (_fileSystem.UnpackBundleCache.IsCached(_options.Bundle.BundleGuid))
_steps = ESteps.LoadUnpackBundle;
else
_steps = ESteps.UnpackFile;
}
else
{
_steps = ESteps.LoadBundle;
}
}
if (_steps == ESteps.UnpackFile)
{
// 中断解压
if (ShouldAbortDownload)
{
if (_unpackFileOp != null)
_unpackFileOp.AbortOperation();
_steps = ESteps.AbortUnpack;
}
}
if (_steps == ESteps.UnpackFile)
{
if (_unpackFileOp == null)
{
// 注意:内置文件解压不做失败尝试
var options = new FSDownloadBundleOptions(_options.Bundle, 0);
_unpackFileOp = _fileSystem.DownloadBundleAsync(options);
_unpackFileOp.StartOperation();
AddChildOperation(_unpackFileOp);
}
if (IsWaitForCompletion)
_unpackFileOp.WaitForCompletion();
_unpackFileOp.UpdateOperation();
if (_unpackFileOp.IsDone == false)
return;
if (_unpackFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadUnpackBundle;
}
else
{
_steps = ESteps.Done;
SetError(_unpackFileOp.Error);
}
}
if (_steps == ESteps.AbortUnpack)
{
if (_unpackFileOp != null)
{
if (IsWaitForCompletion)
_unpackFileOp.WaitForCompletion();
_unpackFileOp.UpdateOperation();
if (_unpackFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
SetError("Bundle download aborted.");
}
if (_steps == ESteps.LoadUnpackBundle)
{
_loadBundleOp = _fileSystem.UnpackBundleCache.LoadBundleAsync(_options.ConvertTo());
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.LoadBundle)
{
_loadBundleOp = _fileSystem.BuiltinBundleCache.LoadBundleAsync(_options.ConvertTo());
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (IsWaitForCompletion)
_loadBundleOp.WaitForCompletion();
_loadBundleOp.UpdateOperation();
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
{
_steps = ESteps.Done;
SetError("Fatal error: loaded bundle handle is null.");
YooLogger.LogError(Error);
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
}
}

View File

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

View File

@@ -0,0 +1,90 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的加载包裹清单操作
/// </summary>
internal sealed class BFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestPackageHash,
LoadPackageManifest,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly string _packageVersion;
private RequestBuiltinPackageHashOperation _requestBuiltinPackageHashOp;
private LoadBuiltinPackageManifestOperation _loadBuiltinPackageManifestOp;
private ESteps _steps = ESteps.None;
internal BFSLoadPackageManifestOperation(BuiltinFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
protected override void InternalStart()
{
_steps = ESteps.RequestPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageHash)
{
if (_requestBuiltinPackageHashOp == null)
{
_requestBuiltinPackageHashOp = new RequestBuiltinPackageHashOperation(_fileSystem, _packageVersion);
_requestBuiltinPackageHashOp.StartOperation();
AddChildOperation(_requestBuiltinPackageHashOp);
}
_requestBuiltinPackageHashOp.UpdateOperation();
if (_requestBuiltinPackageHashOp.IsDone == false)
return;
if (_requestBuiltinPackageHashOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_requestBuiltinPackageHashOp.Error);
}
}
if (_steps == ESteps.LoadPackageManifest)
{
if (_loadBuiltinPackageManifestOp == null)
{
string packageHash = _requestBuiltinPackageHashOp.PackageHash;
_loadBuiltinPackageManifestOp = new LoadBuiltinPackageManifestOperation(_fileSystem, _packageVersion, packageHash);
_loadBuiltinPackageManifestOp.StartOperation();
AddChildOperation(_loadBuiltinPackageManifestOp);
}
_loadBuiltinPackageManifestOp.UpdateOperation();
if (_loadBuiltinPackageManifestOp.IsDone == false)
return;
if (_loadBuiltinPackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
Manifest = _loadBuiltinPackageManifestOp.Manifest;
}
else
{
_steps = ESteps.Done;
SetError(_loadBuiltinPackageManifestOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,61 @@
namespace YooAsset
{
/// <summary>
/// 内置文件系统的查询包裹版本操作
/// </summary>
internal sealed class BFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private RequestBuiltinPackageVersionOperation _requestBuiltinPackageVersionOp;
private ESteps _steps = ESteps.None;
internal BFSRequestPackageVersionOperation(BuiltinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.RequestPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_requestBuiltinPackageVersionOp == null)
{
_requestBuiltinPackageVersionOp = new RequestBuiltinPackageVersionOperation(_fileSystem);
_requestBuiltinPackageVersionOp.StartOperation();
AddChildOperation(_requestBuiltinPackageVersionOp);
}
_requestBuiltinPackageVersionOp.UpdateOperation();
if (_requestBuiltinPackageVersionOp.IsDone == false)
return;
if (_requestBuiltinPackageVersionOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
PackageVersion = _requestBuiltinPackageVersionOp.PackageVersion;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_requestBuiltinPackageVersionOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,142 @@
namespace YooAsset
{
/// <summary>
/// 拷贝内置包裹清单到沙盒操作
/// </summary>
internal sealed class CopyBuiltinPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadBuiltinPackageVersion,
CopyBuiltinPackageHash,
CopyBuiltinPackageManifest,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private RequestBuiltinPackageVersionOperation _requestBuiltinPackageVersionOp;
private CopyBuiltinFileOperation _copyBuiltinHashFileOp;
private CopyBuiltinFileOperation _copyBuiltinManifestFileOp;
private ESteps _steps = ESteps.None;
public CopyBuiltinPackageManifestOperation(BuiltinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.LoadBuiltinPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBuiltinPackageVersion)
{
if (_requestBuiltinPackageVersionOp == null)
{
_requestBuiltinPackageVersionOp = new RequestBuiltinPackageVersionOperation(_fileSystem);
_requestBuiltinPackageVersionOp.StartOperation();
AddChildOperation(_requestBuiltinPackageVersionOp);
}
_requestBuiltinPackageVersionOp.UpdateOperation();
if (_requestBuiltinPackageVersionOp.IsDone == false)
return;
if (_requestBuiltinPackageVersionOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CopyBuiltinPackageHash;
}
else
{
_steps = ESteps.Done;
SetError(_requestBuiltinPackageVersionOp.Error);
}
}
if (_steps == ESteps.CopyBuiltinPackageHash)
{
if (_copyBuiltinHashFileOp == null)
{
// 注意:只负责拷贝文件,不负责校验文件。
string packageVersion = _requestBuiltinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageHashDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuiltinPackageHashFilePath(packageVersion);
_copyBuiltinHashFileOp = new CopyBuiltinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuiltinHashFileOp.StartOperation();
AddChildOperation(_copyBuiltinHashFileOp);
}
_copyBuiltinHashFileOp.UpdateOperation();
if (_copyBuiltinHashFileOp.IsDone == false)
return;
if (_copyBuiltinHashFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CopyBuiltinPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_copyBuiltinHashFileOp.Error);
}
}
if (_steps == ESteps.CopyBuiltinPackageManifest)
{
if (_copyBuiltinManifestFileOp == null)
{
// 注意:只负责拷贝文件,不负责校验文件。
string packageVersion = _requestBuiltinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageManifestDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuiltinPackageManifestFilePath(packageVersion);
_copyBuiltinManifestFileOp = new CopyBuiltinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuiltinManifestFileOp.StartOperation();
AddChildOperation(_copyBuiltinManifestFileOp);
}
_copyBuiltinManifestFileOp.UpdateOperation();
if (_copyBuiltinManifestFileOp.IsDone == false)
return;
if (_copyBuiltinManifestFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_copyBuiltinManifestFileOp.Error);
}
}
}
private string GetCopyManifestFileRoot()
{
string destRoot = _fileSystem.CopyBuiltinPackageManifestDestRoot;
if (string.IsNullOrEmpty(destRoot))
{
string defaultCacheRoot = YooAssetConfiguration.GetDefaultCacheRoot();
destRoot = PathUtility.Combine(defaultCacheRoot, _fileSystem.PackageName, SandboxFileSystemConsts.ManifestFilesFolderName);
}
return destRoot;
}
private string GetCopyPackageHashDestPath(string packageVersion)
{
string fileRoot = GetCopyManifestFileRoot();
string fileName = YooAssetConfiguration.GetPackageHashFileName(_fileSystem.PackageName, packageVersion);
return PathUtility.Combine(fileRoot, fileName);
}
private string GetCopyPackageManifestDestPath(string packageVersion)
{
string fileRoot = GetCopyManifestFileRoot();
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(_fileSystem.PackageName, packageVersion);
return PathUtility.Combine(fileRoot, fileName);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,123 @@
using System;
using System.IO;
namespace YooAsset
{
/// <summary>
/// 拷贝内置文件操作
/// </summary>
internal sealed class CopyBuiltinFileOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckFileExist,
TryCopyFile,
UnpackFile,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly string _sourceFilePath;
private readonly string _destFilePath;
private IDownloadFileRequest _downloadFileRequest;
private ESteps _steps = ESteps.None;
internal CopyBuiltinFileOperation(BuiltinFileSystem fileSystem, string sourceFilePath, string destFilePath)
{
_fileSystem = fileSystem;
_sourceFilePath = sourceFilePath;
_destFilePath = destFilePath;
}
protected override void InternalStart()
{
_steps = ESteps.CheckFileExist;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckFileExist)
{
// 注意:只检查目标文件是否存在,不校验完整性。
// 说明:文件校验由后续的缓存写入流程负责。
if (File.Exists(_destFilePath))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.TryCopyFile;
}
}
if (_steps == ESteps.TryCopyFile)
{
if (File.Exists(_sourceFilePath))
{
try
{
FileUtility.EnsureParentDirectoryExists(_destFilePath);
File.Copy(_sourceFilePath, _destFilePath, true);
_steps = ESteps.Done;
SetResult();
}
catch (Exception ex)
{
YooLogger.LogWarning($"Failed to copy builtin file: {ex.Message}.");
_steps = ESteps.UnpackFile;
}
}
else
{
_steps = ESteps.UnpackFile;
}
}
if (_steps == ESteps.UnpackFile)
{
if (_downloadFileRequest == null)
{
// TODO: 团结引擎在某些安卓机型红米通过UnityWebRequest拷贝包内文件会小概率失败需要借助其它方式来拷贝包内文件。
string url = DownloadUrlHelper.ToLocalFileUrl(_sourceFilePath);
var args = new DownloadFileRequestArgs(
url: url,
timeout: 60,
watchdogTimeout: 0,
savePath: _destFilePath);
_downloadFileRequest = _fileSystem.DownloadBackend.CreateFileRequest(args);
_downloadFileRequest.SendRequest();
}
if (_downloadFileRequest.IsDone == false)
return;
if (_downloadFileRequest.Status == EDownloadRequestStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileRequest.Error);
}
}
}
protected override void InternalDispose()
{
if (_downloadFileRequest != null)
{
_downloadFileRequest.Dispose();
_downloadFileRequest = null;
}
}
protected override void InternalWaitForCompletion()
{
//注意:等待解压本地文件完毕,该操作会挂起主线程!
ExecuteUntilComplete();
}
}
}

View File

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

View File

@@ -0,0 +1,153 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载内置包裹清单文件操作
/// </summary>
internal sealed class LoadBuiltinPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadFileData,
RequestFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private IDownloadBytesRequest _downloadBytesRequest;
private DeserializeManifestOperation _deserializeManifestOp;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { get; private set; }
internal LoadBuiltinPackageManifestOperation(BuiltinFileSystem fileSystem, string packageVersion, string packageHash)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_packageHash = packageHash;
}
protected override void InternalStart()
{
_steps = ESteps.TryLoadFileData;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadFileData)
{
string filePath = _fileSystem.GetBuiltinPackageManifestFilePath(_packageVersion);
if (File.Exists(filePath))
{
try
{
_fileData = File.ReadAllBytes(filePath);
_steps = ESteps.VerifyFileData;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to read builtin package manifest file: {ex.Message}.");
}
}
else
{
_steps = ESteps.RequestFileData;
}
}
if (_steps == ESteps.RequestFileData)
{
if (_downloadBytesRequest == null)
{
string filePath = _fileSystem.GetBuiltinPackageManifestFilePath(_packageVersion);
string url = DownloadUrlHelper.ToLocalFileUrl(filePath);
var args = new DownloadDataRequestArgs(
url: url,
timeout: 60,
watchdogTimeout: 0);
_downloadBytesRequest = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_downloadBytesRequest.SendRequest();
}
if (_downloadBytesRequest.IsDone == false)
return;
if (_downloadBytesRequest.Status == EDownloadRequestStatus.Succeeded)
{
_fileData = _downloadBytesRequest.Result;
_steps = ESteps.VerifyFileData;
}
else
{
_steps = ESteps.Done;
SetError(_downloadBytesRequest.Error);
}
}
if (_steps == ESteps.VerifyFileData)
{
if (PackageManifestHelper.VerifyManifestData(_fileData, _packageHash))
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
SetError("Failed to verify builtin package manifest file.");
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializeManifestOp == null)
{
_deserializeManifestOp = new DeserializeManifestOperation(_fileSystem.ManifestDecryptor, _fileData);
_deserializeManifestOp.StartOperation();
AddChildOperation(_deserializeManifestOp);
}
_deserializeManifestOp.UpdateOperation();
Progress = _deserializeManifestOp.Progress;
if (_deserializeManifestOp.IsDone == false)
return;
if (_deserializeManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
Manifest = _deserializeManifestOp.Manifest;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_deserializeManifestOp.Error);
}
}
}
protected override void InternalDispose()
{
if (_downloadBytesRequest != null)
{
_downloadBytesRequest.Dispose();
_downloadBytesRequest = null;
}
}
protected override string InternalGetDescription()
{
return $"PackageVersion: {_packageVersion} PackageHash: {_packageHash}";
}
}
}

View File

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

View File

@@ -0,0 +1,118 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 请求内置包裹哈希操作
/// </summary>
internal sealed class RequestBuiltinPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadPackageHash,
RequestPackageHash,
CheckResult,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly string _packageVersion;
private IDownloadTextRequest _downloadTextRequest;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { get; private set; }
internal RequestBuiltinPackageHashOperation(BuiltinFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
protected override void InternalStart()
{
_steps = ESteps.TryLoadPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadPackageHash)
{
string filePath = _fileSystem.GetBuiltinPackageHashFilePath(_packageVersion);
if (File.Exists(filePath))
{
try
{
PackageHash = File.ReadAllText(filePath);
_steps = ESteps.CheckResult;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to read builtin package hash file: {ex.Message}.");
}
}
else
{
_steps = ESteps.RequestPackageHash;
}
}
if (_steps == ESteps.RequestPackageHash)
{
if (_downloadTextRequest == null)
{
string filePath = _fileSystem.GetBuiltinPackageHashFilePath(_packageVersion);
string url = DownloadUrlHelper.ToLocalFileUrl(filePath);
var args = new DownloadDataRequestArgs(
url: url,
timeout: 60,
watchdogTimeout: 0);
_downloadTextRequest = _fileSystem.DownloadBackend.CreateTextRequest(args);
_downloadTextRequest.SendRequest();
}
if (_downloadTextRequest.IsDone == false)
return;
if (_downloadTextRequest.Status == EDownloadRequestStatus.Succeeded)
{
PackageHash = _downloadTextRequest.Result;
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.Done;
SetError(_downloadTextRequest.Error);
}
}
if (_steps == ESteps.CheckResult)
{
if (TextUtility.ValidateContent(PackageHash, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Builtin package hash file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
}
protected override void InternalDispose()
{
if (_downloadTextRequest != null)
{
_downloadTextRequest.Dispose();
_downloadTextRequest = null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,116 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 请求内置包裹版本操作
/// </summary>
internal sealed class RequestBuiltinPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadPackageVersion,
RequestPackageVersion,
CheckResult,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private IDownloadTextRequest _downloadTextRequest;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion { get; private set; }
internal RequestBuiltinPackageVersionOperation(BuiltinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.TryLoadPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadPackageVersion)
{
string filePath = _fileSystem.GetBuiltinPackageVersionFilePath();
if (File.Exists(filePath))
{
try
{
PackageVersion = File.ReadAllText(filePath);
_steps = ESteps.CheckResult;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to read builtin package version file: {ex.Message}.");
}
}
else
{
_steps = ESteps.RequestPackageVersion;
}
}
if (_steps == ESteps.RequestPackageVersion)
{
if (_downloadTextRequest == null)
{
string filePath = _fileSystem.GetBuiltinPackageVersionFilePath();
string url = DownloadUrlHelper.ToLocalFileUrl(filePath);
var args = new DownloadDataRequestArgs(
url: url,
timeout: 60,
watchdogTimeout: 0);
_downloadTextRequest = _fileSystem.DownloadBackend.CreateTextRequest(args);
_downloadTextRequest.SendRequest();
}
if (_downloadTextRequest.IsDone == false)
return;
if (_downloadTextRequest.Status == EDownloadRequestStatus.Succeeded)
{
PackageVersion = _downloadTextRequest.Result;
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.Done;
SetError(_downloadTextRequest.Error);
}
}
if (_steps == ESteps.CheckResult)
{
if (TextUtility.ValidateContent(PackageVersion, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Builtin package version file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
}
protected override void InternalDispose()
{
if (_downloadTextRequest != null)
{
_downloadTextRequest.Dispose();
_downloadTextRequest = null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,126 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 解压并缓存文件操作
/// </summary>
internal sealed class UnpackAndCacheFileOperation : DownloadFileBaseOperation
{
private enum ESteps
{
None,
CheckCopy,
CopyLocalFile,
CacheFile,
Done,
}
private readonly BuiltinFileSystem _fileSystem;
private readonly string _builtinFilePath;
private readonly string _tempFilePath;
private CopyBuiltinFileOperation _copyBuiltinFileOp;
private BCWriteCacheOperation _writeCacheOp;
private ESteps _steps = ESteps.None;
internal UnpackAndCacheFileOperation(BuiltinFileSystem fileSystem, PackageBundle bundle, string builtinFilePath) : base(bundle, builtinFilePath)
{
_fileSystem = fileSystem;
_builtinFilePath = builtinFilePath;
_tempFilePath = _fileSystem.GetUnpackTempFilePath(bundle);
}
protected override void InternalStart()
{
_steps = ESteps.CheckCopy;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件拷贝
if (_steps == ESteps.CheckCopy)
{
// 删除历史缓存文件
FileUtility.EnsureParentDirectoryExists(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.CopyLocalFile;
}
// 拷贝本地文件
if (_steps == ESteps.CopyLocalFile)
{
if (_copyBuiltinFileOp == null)
{
_copyBuiltinFileOp = new CopyBuiltinFileOperation(_fileSystem, _builtinFilePath, _tempFilePath);
_copyBuiltinFileOp.StartOperation();
AddChildOperation(_copyBuiltinFileOp);
}
if (IsWaitForCompletion)
_copyBuiltinFileOp.WaitForCompletion();
_copyBuiltinFileOp.UpdateOperation();
if (_copyBuiltinFileOp.IsDone == false)
return;
// 更新下载报告
LatestReport = DownloadReport.CreateProgress(Bundle.FileSize, 1f);
if (_copyBuiltinFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CacheFile;
}
else
{
_steps = ESteps.Done;
SetError(_copyBuiltinFileOp.Error);
// 注意:拷贝失败后直接删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
// 缓存文件
if (_steps == ESteps.CacheFile)
{
if (_writeCacheOp == null)
{
var options = new BCWriteCacheOptions(Bundle, _tempFilePath);
_writeCacheOp = _fileSystem.UnpackBundleCache.WriteCacheAsync(options);
_writeCacheOp.StartOperation();
AddChildOperation(_writeCacheOp);
}
if (IsWaitForCompletion)
_writeCacheOp.WaitForCompletion();
_writeCacheOp.UpdateOperation();
if (_writeCacheOp.IsDone == false)
return;
if (_writeCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_writeCacheOp.Error);
}
// 注意:缓存完成后直接删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,304 @@
using System;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 模拟文件系统,管理编辑器模式下的模拟文件系统。
/// </summary>
internal class EditorFileSystem : IFileSystem
{
/// <summary>
/// 包裹根目录路径
/// </summary>
protected string _packageRoot;
/// <summary>
/// 虚拟文件缓存系统
/// </summary>
public IBundleCache BundleCache { get; private set; }
/// <summary>
/// 下载调度器
/// </summary>
public DownloadSchedulerOperation DownloadScheduler { get; internal set; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; private set; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { get; private set; }
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { get; private set; }
/// <summary>
/// 自定义参数模拟WebGL平台模式
/// </summary>
public bool VirtualWebGLMode { get; private set; } = false;
/// <summary>
/// 自定义参数:模拟虚拟下载模式
/// </summary>
public bool VirtualDownloadMode { get; private set; } = false;
/// <summary>
/// 自定义参数:模拟虚拟下载的网速(单位:字节)
/// </summary>
/// <remarks>
/// 默认值1024
/// </remarks>
public int VirtualDownloadSpeed { get; private set; } = 1024;
/// <summary>
/// 自定义参数:最大并发连接数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32。过大的并发数可能被服务器限流也会增加本地资源消耗。
/// </remarks>
public int DownloadMaxConcurrency { get; private set; } = 8;
/// <summary>
/// 自定义参数:每帧发起的最大请求数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32。避免单帧发起过多请求导致卡顿。
/// </remarks>
public int DownloadMaxRequestsPerFrame { get; private set; } = 8;
/// <summary>
/// 自定义参数:异步模拟加载最小帧数
/// </summary>
/// <remarks>
/// 默认值1
/// </remarks>
public int AsyncSimulateMinFrame { get; private set; } = 1;
/// <summary>
/// 自定义参数:异步模拟加载最大帧数
/// </summary>
/// <remarks>
/// 默认值1
/// </remarks>
public int AsyncSimulateMaxFrame { get; private set; } = 1;
#endregion
/// <summary>
/// 创建实例
/// </summary>
public EditorFileSystem()
{
}
/// <inheritdoc />
public FSInitializeOperation InitializeAsync()
{
var operation = new EFSInitializeOperation(this);
return operation;
}
/// <inheritdoc />
public FSRequestPackageVersionOperation RequestPackageVersionAsync(FSRequestPackageVersionOptions options)
{
var operation = new EFSRequestPackageVersionOperation(this);
return operation;
}
/// <inheritdoc />
public FSLoadPackageManifestOperation LoadPackageManifestAsync(FSLoadPackageManifestOptions options)
{
var operation = new EFSLoadPackageManifestOperation(this, options.PackageVersion);
return operation;
}
/// <inheritdoc />
public FSLoadPackageBundleOperation LoadPackageBundleAsync(FSLoadPackageBundleOptions options)
{
var operation = new EFSLoadPackageBundleOperation(this, options);
return operation;
}
/// <inheritdoc />
public FSDownloadBundleOperation DownloadBundleAsync(FSDownloadBundleOptions options)
{
var downloader = new EFSDownloadBundleOperation(this, options);
return downloader;
}
/// <inheritdoc />
public FSClearCacheOperation ClearCacheAsync(FSClearCacheOptions options)
{
if (options.ClearMethod == ClearCacheMethods.ClearAllManifestFiles)
{
var operation = new FSClearCacheCompleteOperation();
return operation;
}
else if (options.ClearMethod == ClearCacheMethods.ClearUnusedManifestFiles)
{
var operation = new FSClearCacheCompleteOperation();
return operation;
}
else
{
var operation = new EFSClearCacheOperation(this, options);
return operation;
}
}
/// <inheritdoc />
public void SetParameter(string paramName, object value)
{
if (paramName == nameof(EFileSystemParameter.DownloadBackend))
{
DownloadBackend = FileSystemHelper.CastParameter<IDownloadBackend>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.UnityWebRequestCreator))
{
WebRequestCreator = FileSystemHelper.CastParameter<UnityWebRequestCreator>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.VirtualWebglMode))
{
VirtualWebGLMode = FileSystemHelper.CastParameter<bool>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.VirtualDownloadMode))
{
VirtualDownloadMode = FileSystemHelper.CastParameter<bool>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.VirtualDownloadSpeed))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
VirtualDownloadSpeed = Mathf.Clamp(convertValue, 1, int.MaxValue);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxConcurrency))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_CONCURRENCY value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
DownloadMaxConcurrency = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxRequestPerFrame))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_REQUEST_PER_FRAME value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
DownloadMaxRequestsPerFrame = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.AsyncSimulateMinFrame))
{
AsyncSimulateMinFrame = FileSystemHelper.CastParameter<int>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AsyncSimulateMaxFrame))
{
AsyncSimulateMaxFrame = FileSystemHelper.CastParameter<int>(paramName, value);
}
else
{
throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName));
}
}
/// <inheritdoc />
public void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
throw new System.ArgumentException($"{nameof(EditorFileSystem)} package root is null or empty.");
_packageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
// 创建编辑器文件缓存系统
if (AsyncSimulateMinFrame > AsyncSimulateMaxFrame)
AsyncSimulateMinFrame = AsyncSimulateMaxFrame;
var cacheConfig = new EditorBundleCache.Configuration(
virtualDownloadMode: VirtualDownloadMode,
virtualWebGLMode: VirtualWebGLMode,
asyncSimulateMinFrame: AsyncSimulateMinFrame,
asyncSimulateMaxFrame: AsyncSimulateMaxFrame);
BundleCache = new EditorBundleCache(packageName, _packageRoot, cacheConfig);
}
/// <inheritdoc />
public void OnDestroy()
{
if (BundleCache != null)
{
BundleCache.Dispose();
BundleCache = null;
}
if (DownloadScheduler != null)
{
DownloadScheduler.AbortOperation();
DownloadScheduler = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
/// <inheritdoc />
public bool CanAcceptBundle(PackageBundle bundle)
{
return true;
}
/// <inheritdoc />
public bool IsDownloadRequired(PackageBundle bundle)
{
return BundleCache.IsCached(bundle.BundleGuid) == false;
}
/// <inheritdoc />
public bool IsUnpackRequired(PackageBundle bundle)
{
return false;
}
/// <inheritdoc />
public bool IsImportRequired(PackageBundle bundle)
{
return false;
}
#region
/// <summary>
/// 获取编辑器包裹版本文件路径
/// </summary>
public string GetEditorPackageVersionFilePath()
{
string fileName = YooAssetConfiguration.GetPackageVersionFileName(PackageName);
return PathUtility.Combine(_packageRoot, fileName);
}
/// <summary>
/// 获取编辑器包裹哈希文件路径
/// </summary>
public string GetEditorPackageHashFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
/// <summary>
/// 获取编辑器包裹清单文件路径
/// </summary>
public string GetEditorPackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,21 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统工具类
/// </summary>
internal static class EditorFileSystemHelper
{
/// <summary>
/// 获取编辑器环境下资源包对应的源文件路径
/// </summary>
public static string GetEditorFilePath(PackageBundle bundle)
{
if (bundle.MainAssets.Count == 0)
return string.Empty;
var packageAsset = bundle.MainAssets[0];
return packageAsset.AssetPath;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,74 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的清理缓存操作
/// </summary>
internal sealed class EFSClearCacheOperation : FSClearCacheOperation
{
private enum ESteps
{
None,
CheckReadOnly,
ClearCache,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly FSClearCacheOptions _options;
private BCClearCacheOperation _clearCacheOp;
private ESteps _steps = ESteps.None;
internal EFSClearCacheOperation(EditorFileSystem fileSystem, FSClearCacheOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.CheckReadOnly;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckReadOnly)
{
if (_fileSystem.BundleCache.IsReadOnly)
{
_steps = ESteps.Done;
SetResult();
return;
}
_steps = ESteps.ClearCache;
}
if (_steps == ESteps.ClearCache)
{
if (_clearCacheOp == null)
{
_clearCacheOp = _fileSystem.BundleCache.ClearCacheAsync(_options.ConvertTo());
_clearCacheOp.StartOperation();
AddChildOperation(_clearCacheOp);
}
_clearCacheOp.UpdateOperation();
if (_clearCacheOp.IsDone == false)
return;
if (_clearCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_clearCacheOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,123 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的下载文件操作
/// </summary>
internal sealed class EFSDownloadBundleOperation : FSDownloadBundleOperation
{
private enum ESteps
{
None,
CheckExists,
CreateDownload,
CheckDownload,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly FSDownloadBundleOptions _options;
private DownloadFileBaseOperation _downloadFileOp;
private ESteps _steps = ESteps.None;
internal EFSDownloadBundleOperation(EditorFileSystem fileSystem, FSDownloadBundleOptions options) : base(options.Bundle)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.CheckExists;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.BundleCache.IsCached(_options.Bundle.BundleGuid))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.CreateDownload;
}
}
// 创建下载器
if (_steps == ESteps.CreateDownload)
{
_downloadFileOp = _fileSystem.DownloadScheduler.TryGetDownloadOperation(Bundle);
if (_downloadFileOp == null)
{
string editorFilePath = EditorFileSystemHelper.GetEditorFilePath(Bundle);
if (string.IsNullOrEmpty(editorFilePath))
{
_steps = ESteps.Done;
SetError($"Editor file path is empty for bundle '{Bundle.BundleName}'.");
return;
}
_downloadFileOp = new SimulateAndCacheFileOperation(_fileSystem, Bundle, editorFilePath);
_fileSystem.DownloadScheduler.RegisterDownloadOperation(_downloadFileOp);
}
_steps = ESteps.CheckDownload;
}
// 检测结果
if (_steps == ESteps.CheckDownload)
{
if (IsWaitForCompletion)
{
if (_downloadFileOp is SimulateAndCacheFileOperation)
{
_steps = ESteps.Done;
SetError($"Attempting to load bundle '{Bundle.BundleName}' from simulate: '{_downloadFileOp.Url}'.");
return;
}
_downloadFileOp.WaitForCompletion();
}
// 注意:不主动调用 _downloadFileOp.UpdateOperation()
// 说明:下载任务由 DownloadSchedulerOperation 统一驱动,此处仅读取状态。
// 说明:同步等待由 WaitForCompletion() 内部的 ExecuteBatch() 保证。
Progress = _downloadFileOp.Progress;
Report = _downloadFileOp.LatestReport;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
protected override void InternalAbort()
{
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_downloadFileOp != null)
{
_downloadFileOp.Release();
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,90 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的初始化操作
/// </summary>
internal sealed class EFSInitializeOperation : FSInitializeOperation
{
private enum ESteps
{
None,
CheckPlatform,
InitializeBundleCache,
CreateScheduler,
Done,
}
private readonly EditorFileSystem _fileSystem;
private BCInitializeOperation _initializeBundleCacheOp;
private ESteps _steps = ESteps.None;
internal EFSInitializeOperation(EditorFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.CheckPlatform;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckPlatform)
{
#if !UNITY_EDITOR
_steps = ESteps.Done;
SetError($"{nameof(EditorFileSystem)} only supports the Unity Editor.");
#else
_steps = ESteps.InitializeBundleCache;
#endif
}
if (_steps == ESteps.InitializeBundleCache)
{
if (_initializeBundleCacheOp == null)
{
_initializeBundleCacheOp = _fileSystem.BundleCache.InitializeAsync();
_initializeBundleCacheOp.StartOperation();
AddChildOperation(_initializeBundleCacheOp);
}
_initializeBundleCacheOp.UpdateOperation();
Progress = _initializeBundleCacheOp.Progress;
if (_initializeBundleCacheOp.IsDone == false)
return;
if (_initializeBundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CreateScheduler;
}
else
{
_steps = ESteps.Done;
SetError(_initializeBundleCacheOp.Error);
}
}
if (_steps == ESteps.CreateScheduler)
{
// 注意: 下载调度中心在最后一步创建,防止初始化失败后残留任务。
// 注意: 下载调度中心作为独立任务运行!
if (_fileSystem.DownloadScheduler == null)
{
var schedulerConfig = new DownloadSchedulerOperation.Configuration(
schedulerName: _fileSystem.GetType().Name,
downloadBackend: _fileSystem.DownloadBackend,
maxConcurrency: _fileSystem.DownloadMaxConcurrency,
maxRequestsPerFrame: _fileSystem.DownloadMaxRequestsPerFrame);
_fileSystem.DownloadScheduler = new DownloadSchedulerOperation(schedulerConfig);
AsyncOperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.DownloadScheduler);
}
_steps = ESteps.Done;
SetResult();
}
}
}
}

View File

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

View File

@@ -0,0 +1,149 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的加载资源包操作
/// </summary>
internal sealed class EFSLoadPackageBundleOperation : FSLoadPackageBundleOperation
{
private enum ESteps
{
None,
Prepare,
DownloadFile,
AbortDownload,
LoadBundle,
CheckResult,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly FSLoadPackageBundleOptions _options;
private FSDownloadBundleOperation _downloadFileOp;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal EFSLoadPackageBundleOperation(EditorFileSystem fileSystem, FSLoadPackageBundleOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.Prepare;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
if (_fileSystem.BundleCache.IsCached(_options.Bundle.BundleGuid))
_steps = ESteps.LoadBundle;
else
_steps = ESteps.DownloadFile;
}
if (_steps == ESteps.DownloadFile)
{
// 中断下载
if (ShouldAbortDownload)
{
if (_downloadFileOp != null)
_downloadFileOp.AbortOperation();
_steps = ESteps.AbortDownload;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
// 注意:模拟文件下载不做失败尝试
var options = new FSDownloadBundleOptions(_options.Bundle, 0);
_downloadFileOp = _fileSystem.DownloadBundleAsync(options);
_downloadFileOp.StartOperation();
AddChildOperation(_downloadFileOp);
}
if (IsWaitForCompletion)
_downloadFileOp.WaitForCompletion();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadBundle;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
}
}
if (_steps == ESteps.AbortDownload)
{
if (_downloadFileOp != null)
{
if (IsWaitForCompletion)
_downloadFileOp.WaitForCompletion();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
SetError("Bundle download aborted.");
}
if (_steps == ESteps.LoadBundle)
{
_loadBundleOp = _fileSystem.BundleCache.LoadBundleAsync(_options.ConvertTo());
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (IsWaitForCompletion)
_loadBundleOp.WaitForCompletion();
_loadBundleOp.UpdateOperation();
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
{
_steps = ESteps.Done;
SetError("Fatal error: loaded bundle handle is null.");
YooLogger.LogError(Error);
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
}
}

View File

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

View File

@@ -0,0 +1,91 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的加载包裹清单操作
/// </summary>
internal sealed class EFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
LoadPackageHash,
LoadPackageManifest,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly string _packageVersion;
private LoadEditorPackageHashOperation _loadEditorPackageHashOp;
private LoadEditorPackageManifestOperation _loadEditorPackageManifestOp;
private ESteps _steps = ESteps.None;
internal EFSLoadPackageManifestOperation(EditorFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
protected override void InternalStart()
{
_steps = ESteps.LoadPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadPackageHash)
{
if (_loadEditorPackageHashOp == null)
{
_loadEditorPackageHashOp = new LoadEditorPackageHashOperation(_fileSystem, _packageVersion);
_loadEditorPackageHashOp.StartOperation();
AddChildOperation(_loadEditorPackageHashOp);
}
_loadEditorPackageHashOp.UpdateOperation();
if (_loadEditorPackageHashOp.IsDone == false)
return;
if (_loadEditorPackageHashOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_loadEditorPackageHashOp.Error);
}
}
if (_steps == ESteps.LoadPackageManifest)
{
if (_loadEditorPackageManifestOp == null)
{
string packageHash = _loadEditorPackageHashOp.PackageHash;
_loadEditorPackageManifestOp = new LoadEditorPackageManifestOperation(_fileSystem, _packageVersion, packageHash);
_loadEditorPackageManifestOp.StartOperation();
AddChildOperation(_loadEditorPackageManifestOp);
}
_loadEditorPackageManifestOp.UpdateOperation();
Progress = _loadEditorPackageManifestOp.Progress;
if (_loadEditorPackageManifestOp.IsDone == false)
return;
if (_loadEditorPackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
Manifest = _loadEditorPackageManifestOp.Manifest;
}
else
{
_steps = ESteps.Done;
SetError(_loadEditorPackageManifestOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,61 @@
namespace YooAsset
{
/// <summary>
/// 编辑器文件系统的查询包裹版本操作
/// </summary>
internal sealed class EFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
LoadPackageVersion,
Done,
}
private readonly EditorFileSystem _fileSystem;
private LoadEditorPackageVersionOperation _loadEditorPackageVersionOp;
private ESteps _steps = ESteps.None;
internal EFSRequestPackageVersionOperation(EditorFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.LoadPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadPackageVersion)
{
if (_loadEditorPackageVersionOp == null)
{
_loadEditorPackageVersionOp = new LoadEditorPackageVersionOperation(_fileSystem);
_loadEditorPackageVersionOp.StartOperation();
AddChildOperation(_loadEditorPackageVersionOp);
}
_loadEditorPackageVersionOp.UpdateOperation();
if (_loadEditorPackageVersionOp.IsDone == false)
return;
if (_loadEditorPackageVersionOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
PackageVersion = _loadEditorPackageVersionOp.PackageVersion;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_loadEditorPackageVersionOp.Error);
}
}
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,66 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载编辑器包裹哈希文件操作
/// </summary>
internal sealed class LoadEditorPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadHash,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly string _packageVersion;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { get; private set; }
internal LoadEditorPackageHashOperation(EditorFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
protected override void InternalStart()
{
_steps = ESteps.LoadHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadHash)
{
string hashFilePath = _fileSystem.GetEditorPackageHashFilePath(_packageVersion);
if (File.Exists(hashFilePath))
{
PackageHash = FileUtility.ReadAllText(hashFilePath);
if (TextUtility.ValidateContent(PackageHash, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Simulation package hash file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
else
{
_steps = ESteps.Done;
SetError($"Could not find simulation package hash file: '{hashFilePath}'.");
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,115 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载编辑器包裹清单文件操作
/// </summary>
internal sealed class LoadEditorPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly EditorFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private DeserializeManifestOperation _deserializeManifestOp;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { get; private set; }
internal LoadEditorPackageManifestOperation(EditorFileSystem fileSystem, string packageVersion, string packageHash)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_packageHash = packageHash;
}
protected override void InternalStart()
{
_steps = ESteps.LoadFileData;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadFileData)
{
string manifestFilePath = _fileSystem.GetEditorPackageManifestFilePath(_packageVersion);
if (File.Exists(manifestFilePath))
{
try
{
_fileData = FileUtility.ReadAllBytes(manifestFilePath);
_steps = ESteps.VerifyFileData;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to read editor package manifest file: {ex.Message}.");
}
}
else
{
_steps = ESteps.Done;
SetError($"Could not find simulation package manifest file: '{manifestFilePath}'.");
}
}
if (_steps == ESteps.VerifyFileData)
{
if (PackageManifestHelper.VerifyManifestData(_fileData, _packageHash))
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
SetError("Failed to verify simulation package manifest file.");
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializeManifestOp == null)
{
_deserializeManifestOp = new DeserializeManifestOperation(null, _fileData);
_deserializeManifestOp.StartOperation();
AddChildOperation(_deserializeManifestOp);
}
_deserializeManifestOp.UpdateOperation();
Progress = _deserializeManifestOp.Progress;
if (_deserializeManifestOp.IsDone == false)
return;
if (_deserializeManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
Manifest = _deserializeManifestOp.Manifest;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_deserializeManifestOp.Error);
}
}
}
protected override string InternalGetDescription()
{
return $"PackageVersion: {_packageVersion} PackageHash: {_packageHash}";
}
}
}

View File

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

View File

@@ -0,0 +1,64 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载编辑器包裹版本文件操作
/// </summary>
internal sealed class LoadEditorPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadVersion,
Done,
}
private readonly EditorFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion { get; private set; }
internal LoadEditorPackageVersionOperation(EditorFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.LoadVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadVersion)
{
string versionFilePath = _fileSystem.GetEditorPackageVersionFilePath();
if (File.Exists(versionFilePath))
{
PackageVersion = FileUtility.ReadAllText(versionFilePath);
if (TextUtility.ValidateContent(PackageVersion, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Simulation package version file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
else
{
_steps = ESteps.Done;
SetError($"Could not find simulation package version file: '{versionFilePath}'.");
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,113 @@
namespace YooAsset
{
/// <summary>
/// 模拟下载并缓存文件操作
/// </summary>
internal sealed class SimulateAndCacheFileOperation : DownloadFileBaseOperation
{
private enum ESteps
{
None,
CreateRequest,
CheckRequest,
CacheFile,
Done,
}
private readonly EditorFileSystem _fileSystem;
private IDownloadRequest _downloadRequest;
private BCWriteCacheOperation _writeCacheOp;
private ESteps _steps = ESteps.None;
internal SimulateAndCacheFileOperation(EditorFileSystem fileSystem, PackageBundle bundle, string filePath) : base(bundle, filePath)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载请求
if (_steps == ESteps.CreateRequest)
{
int speed = _fileSystem.VirtualDownloadSpeed;
var args = new SimulatedDownloadRequestArgs(
url: Url,
fileSize: Bundle.FileSize,
downloadSpeed: speed);
_downloadRequest = _fileSystem.DownloadBackend.CreateSimulateRequest(args);
_downloadRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
LatestReport = DownloadReport.CreateProgress(_downloadRequest.DownloadedBytes, _downloadRequest.DownloadProgress);
Progress = _downloadRequest.DownloadProgress;
if (_downloadRequest.IsDone == false)
return;
// 更新下载报告
LatestReport = DownloadReport.CreateFinished(_downloadRequest.HttpCode, _downloadRequest.HttpError,
_downloadRequest.DownloadedBytes, _downloadRequest.DownloadProgress);
// 检查网络错误
if (_downloadRequest.Status == EDownloadRequestStatus.Succeeded)
{
_steps = ESteps.CacheFile;
}
else
{
_steps = ESteps.Done;
SetError(_downloadRequest.Error);
}
}
// 缓存文件
if (_steps == ESteps.CacheFile)
{
if (_writeCacheOp == null)
{
var options = new BCWriteCacheOptions(Bundle, Url);
_writeCacheOp = _fileSystem.BundleCache.WriteCacheAsync(options);
_writeCacheOp.StartOperation();
AddChildOperation(_writeCacheOp);
}
_writeCacheOp.UpdateOperation();
if (_writeCacheOp.IsDone == false)
return;
if (_writeCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_writeCacheOp.Error);
}
}
}
protected override void InternalDispose()
{
if (_downloadRequest != null)
{
_downloadRequest.Dispose();
_downloadRequest = null;
}
}
protected override void InternalWaitForCompletion()
{
throw new YooInternalException($"{nameof(SimulateAndCacheFileOperation)} does not support synchronous waiting. Bundle: '{Bundle.BundleName}', Url: '{Url}'.");
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,80 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 应用程序水印
/// </summary>
internal class ApplicationFootprint
{
private readonly string _filePath;
private string _footprint;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="filePath">足迹文件路径</param>
public ApplicationFootprint(string filePath)
{
_filePath = filePath;
}
/// <summary>
/// 读取应用程序水印
/// </summary>
public void Load(string packageName)
{
if (File.Exists(_filePath))
{
try
{
_footprint = FileUtility.ReadAllText(_filePath);
}
catch (System.Exception ex)
{
_footprint = string.Empty;
YooLogger.LogError($"Failed to read application footprint file: {ex.Message}.");
}
}
else
{
Overwrite(packageName);
}
}
/// <summary>
/// 检测水印是否发生变化
/// </summary>
public bool IsDirty()
{
return _footprint != GetApplicationIdentifier();
}
/// <summary>
/// 覆盖掉水印
/// </summary>
public void Overwrite(string packageName)
{
_footprint = GetApplicationIdentifier();
try
{
FileUtility.WriteAllText(_filePath, _footprint);
YooLogger.Log($"Saved application footprint: '{_footprint}'.");
}
catch (System.Exception ex)
{
YooLogger.LogWarning($"Failed to save application footprint file: {ex.Message}.");
}
}
private static string GetApplicationIdentifier()
{
#if UNITY_EDITOR
return Application.version;
#else
return Application.buildGUID;
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,29 @@

namespace YooAsset
{
/// <summary>
/// 覆盖安装清理模式
/// </summary>
public enum EInstallCleanupMode
{
/// <summary>
/// 不做任何处理
/// </summary>
None = 0,
/// <summary>
/// 清理所有缓存文件(包含资源文件和清单文件)
/// </summary>
ClearAllCacheFiles = 1,
/// <summary>
/// 清理所有缓存的资源文件
/// </summary>
ClearAllBundleFiles = 2,
/// <summary>
/// 清理所有缓存的清单文件
/// </summary>
ClearAllManifestFiles = 3,
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,202 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 下载远端资源并缓存到本地的操作
/// </summary>
/// <remarks>
/// TODO: 下载和缓存不能拆分因为FSDownloadBundleOperation下载任务并不唯一会造成写入缓存冲突。
/// </remarks>
internal sealed class DownloadAndCacheFileOperation : DownloadFileBaseOperation
{
private enum ESteps
{
None,
CreateRequest,
CheckRequest,
CacheFile,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _tempFilePath;
private IDownloadFileRequest _downloadFileRequest;
private BCWriteCacheOperation _writeCacheOp;
private bool _enableResume;
private long _fileOriginLength = 0;
private ESteps _steps = ESteps.None;
internal DownloadAndCacheFileOperation(SandboxFileSystem fileSystem, PackageBundle bundle, string url) : base(bundle, url)
{
_fileSystem = fileSystem;
_tempFilePath = _fileSystem.GetTempFilePath(bundle);
}
protected override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载请求
if (_steps == ESteps.CreateRequest)
{
FileUtility.EnsureParentDirectoryExists(_tempFilePath);
_enableResume = Bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize;
if (_enableResume)
{
_downloadFileRequest = CreateResumeRequest();
_downloadFileRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
else
{
_downloadFileRequest = CreateNormalRequest();
_downloadFileRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
bool isDone = _downloadFileRequest.IsDone;
if (_enableResume)
{
long downloadedBytes = _fileOriginLength + _downloadFileRequest.DownloadedBytes;
float downloadProgress = (float)((double)downloadedBytes / Bundle.FileSize);
LatestReport = DownloadReport.CreateProgress(downloadedBytes, downloadProgress);
Progress = downloadProgress;
}
else
{
LatestReport = DownloadReport.CreateProgress(_downloadFileRequest.DownloadedBytes, _downloadFileRequest.DownloadProgress);
Progress = _downloadFileRequest.DownloadProgress;
}
if (isDone == false)
return;
// 更新下载报告
LatestReport = DownloadReport.CreateFinished(_downloadFileRequest.HttpCode, _downloadFileRequest.HttpError,
LatestReport.DownloadedBytes, LatestReport.DownloadProgress);
// 检查网络错误
if (_downloadFileRequest.Status == EDownloadRequestStatus.Succeeded)
{
_steps = ESteps.CacheFile;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileRequest.Error);
if (_enableResume)
{
// 注意: HTTP 416 Range Not Satisfiable 表示服务器无法满足客户端在 Range 请求头中指定的字节范围请求。
if (_downloadFileRequest.HttpCode == 416)
DeleteTempFile();
}
else
{
DeleteTempFile();
}
}
}
// 缓存文件
if (_steps == ESteps.CacheFile)
{
if (_writeCacheOp == null)
{
var options = new BCWriteCacheOptions(Bundle, _tempFilePath);
_writeCacheOp = _fileSystem.BundleCache.WriteCacheAsync(options);
_writeCacheOp.StartOperation();
AddChildOperation(_writeCacheOp);
}
_writeCacheOp.UpdateOperation();
if (_writeCacheOp.IsDone == false)
return;
if (_writeCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_writeCacheOp.Error);
}
// 注意:缓存完成后直接删除临时文件
DeleteTempFile();
}
}
protected override void InternalDispose()
{
if (_downloadFileRequest != null)
{
_downloadFileRequest.Dispose();
_downloadFileRequest = null;
}
}
protected override void InternalWaitForCompletion()
{
throw new YooInternalException($"{nameof(DownloadAndCacheFileOperation)} does not support synchronous waiting. Bundle: '{Bundle.BundleName}', Url: '{Url}'.");
}
private IDownloadFileRequest CreateResumeRequest()
{
// 获取下载起始位置
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= Bundle.FileSize)
{
DeleteTempFile();
}
else
{
_fileOriginLength = fileInfo.Length;
}
}
int watchdogTime = _fileSystem.DownloadWatchdogTimeout;
int timeout = 0; //注意:文件下载不做超时检测
bool appendToFile = true;
bool removeFileOnAbort = false;
long resumeOffset = _fileOriginLength;
var args = new DownloadFileRequestArgs(
url: Url,
timeout: timeout,
watchdogTimeout: watchdogTime,
savePath: _tempFilePath,
appendToFile: appendToFile,
removeFileOnAbort: removeFileOnAbort,
resumeOffset: resumeOffset);
return _fileSystem.DownloadBackend.CreateFileRequest(args);
}
private IDownloadFileRequest CreateNormalRequest()
{
DeleteTempFile();
int watchdogTime = _fileSystem.DownloadWatchdogTimeout;
int timeout = 0; //注意:文件下载不做超时检测
var args = new DownloadFileRequestArgs(
url: Url,
timeout: timeout,
watchdogTimeout: watchdogTime,
savePath: _tempFilePath);
return _fileSystem.DownloadBackend.CreateFileRequest(args);
}
private void DeleteTempFile()
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
}

View File

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

View File

@@ -0,0 +1,155 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 下载包裹哈希文件操作
/// </summary>
internal sealed class DownloadPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckExists,
DownloadFile,
VerifyFile,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private IDownloadFileRequest _downloadFileRequest;
private string _savePath;
private string _tempPath;
private ESteps _steps = ESteps.None;
internal DownloadPackageHashOperation(SandboxFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
protected override void InternalStart()
{
_savePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
_tempPath = _savePath + ".tmp";
_steps = ESteps.CheckExists;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExists)
{
if (File.Exists(_savePath))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileRequest == null)
{
FileUtility.EnsureParentDirectoryExists(_tempPath);
// 删除历史临时文件
if (File.Exists(_tempPath))
File.Delete(_tempPath);
string fileName = YooAssetConfiguration.GetPackageHashFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetWebRequestUrl(fileName);
int watchdogTime = _fileSystem.DownloadWatchdogTimeout;
var args = new DownloadFileRequestArgs(
url: webURL,
timeout: _timeout,
watchdogTimeout: watchdogTime,
savePath: _tempPath);
_downloadFileRequest = _fileSystem.DownloadBackend.CreateFileRequest(args);
_downloadFileRequest.SendRequest();
}
if (_downloadFileRequest.IsDone == false)
return;
if (_downloadFileRequest.Status == EDownloadRequestStatus.Succeeded)
{
_steps = ESteps.VerifyFile;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileRequest.Error);
_fileSystem.DownloadUrlPolicy.OnRequestFailed(_downloadFileRequest.Url, _downloadFileRequest.HttpCode, _downloadFileRequest.HttpError);
DeleteTempFile();
}
}
if (_steps == ESteps.VerifyFile)
{
// 验证临时文件存在且大小有效
FileInfo fileInfo = new FileInfo(_tempPath);
if (fileInfo.Exists == false || fileInfo.Length == 0)
{
_steps = ESteps.Done;
SetError("Downloaded package hash temp file is invalid.");
DeleteTempFile();
return;
}
// 严格校验临时文件内容
string content = FileUtility.ReadAllText(_tempPath);
if (TextUtility.ValidateContent(content, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Downloaded package hash file validation failed: {validateError}.");
DeleteTempFile();
return;
}
// 原子移动到最终缓存路径
try
{
if (File.Exists(_savePath))
File.Delete(_savePath);
File.Move(_tempPath, _savePath);
_steps = ESteps.Done;
SetResult();
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to move hash temp file to cache path: {ex.Message}.");
DeleteTempFile();
}
}
}
protected override void InternalDispose()
{
if (_downloadFileRequest != null)
{
_downloadFileRequest.Dispose();
_downloadFileRequest = null;
}
}
private void DeleteTempFile()
{
if (File.Exists(_tempPath))
File.Delete(_tempPath);
}
private string GetWebRequestUrl(string fileName)
{
var urls = _fileSystem.RemoteService.GetRemoteUrls(fileName);
return _fileSystem.DownloadUrlPolicy.SelectUrl(urls);
}
}
}

View File

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

View File

@@ -0,0 +1,152 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 下载包裹清单文件操作
/// </summary>
internal sealed class DownloadPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckExists,
DownloadFile,
VerifyFile,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private IDownloadFileRequest _downloadFileRequest;
private string _savePath;
private string _tempPath;
private ESteps _steps = ESteps.None;
internal DownloadPackageManifestOperation(SandboxFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
protected override void InternalStart()
{
_savePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
_tempPath = _savePath + ".tmp";
_steps = ESteps.CheckExists;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExists)
{
if (File.Exists(_savePath))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileRequest == null)
{
FileUtility.EnsureParentDirectoryExists(_tempPath);
// 删除历史临时文件
if (File.Exists(_tempPath))
File.Delete(_tempPath);
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetDownloadRequestUrl(fileName);
int watchdogTime = _fileSystem.DownloadWatchdogTimeout;
var args = new DownloadFileRequestArgs(
url: webURL,
timeout: _timeout,
watchdogTimeout: watchdogTime,
savePath: _tempPath);
_downloadFileRequest = _fileSystem.DownloadBackend.CreateFileRequest(args);
_downloadFileRequest.SendRequest();
}
if (_downloadFileRequest.IsDone == false)
return;
if (_downloadFileRequest.Status == EDownloadRequestStatus.Succeeded)
{
_steps = ESteps.VerifyFile;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileRequest.Error);
_fileSystem.DownloadUrlPolicy.OnRequestFailed(_downloadFileRequest.Url, _downloadFileRequest.HttpCode, _downloadFileRequest.HttpError);
DeleteTempFile();
}
}
if (_steps == ESteps.VerifyFile)
{
// 验证临时文件存在且大小有效
FileInfo fileInfo = new FileInfo(_tempPath);
if (fileInfo.Exists == false)
{
_steps = ESteps.Done;
SetError("Downloaded package manifest temp file does not exist.");
return;
}
if (fileInfo.Length < PackageManifestConsts.MinFileSize)
{
_steps = ESteps.Done;
SetError($"Downloaded manifest file is too small ({fileInfo.Length} bytes), possibly corrupted.");
DeleteTempFile();
return;
}
// 原子移动到最终缓存路径
try
{
if (File.Exists(_savePath))
File.Delete(_savePath);
File.Move(_tempPath, _savePath);
_steps = ESteps.Done;
SetResult();
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to move manifest temp file to cache path: {ex.Message}.");
DeleteTempFile();
}
}
}
protected override void InternalDispose()
{
if (_downloadFileRequest != null)
{
_downloadFileRequest.Dispose();
_downloadFileRequest = null;
}
}
private void DeleteTempFile()
{
if (File.Exists(_tempPath))
File.Delete(_tempPath);
}
private string GetDownloadRequestUrl(string fileName)
{
var urls = _fileSystem.RemoteService.GetRemoteUrls(fileName);
return _fileSystem.DownloadUrlPolicy.SelectUrl(urls);
}
}
}

View File

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

View File

@@ -0,0 +1,124 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 导入并缓存文件操作
/// </summary>
internal sealed class ImportAndCacheFileOperation : DownloadFileBaseOperation
{
private enum ESteps
{
None,
CheckTempFile,
CopyLocalFile,
CacheFile,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _sourceFilePath;
private readonly string _tempFilePath;
private BCWriteCacheOperation _bundleCacheOp;
private ESteps _steps = ESteps.None;
internal ImportAndCacheFileOperation(SandboxFileSystem fileSystem, PackageBundle bundle, string sourceFilePath) : base(bundle, sourceFilePath)
{
_fileSystem = fileSystem;
_sourceFilePath = sourceFilePath;
_tempFilePath = _fileSystem.GetTempFilePath(bundle);
}
protected override void InternalStart()
{
_steps = ESteps.CheckTempFile;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测临时文件
if (_steps == ESteps.CheckTempFile)
{
// 删除历史临时文件
FileUtility.EnsureParentDirectoryExists(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.CopyLocalFile;
}
// 拷贝本地文件
if (_steps == ESteps.CopyLocalFile)
{
try
{
File.Copy(_sourceFilePath, _tempFilePath, true);
// 更新下载报告
LatestReport = DownloadReport.CreateProgress(Bundle.FileSize, 1f);
_steps = ESteps.CacheFile;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
LatestReport = DownloadReport.CreateFinished(-1, ex.Message, 0, 0f);
SetError($"Failed to copy local file: {ex.Message}.");
// 注意:拷贝失败后直接删除临时文件
DeleteTempFile();
}
}
// 缓存文件
if (_steps == ESteps.CacheFile)
{
if (_bundleCacheOp == null)
{
var options = new BCWriteCacheOptions(Bundle, _tempFilePath);
_bundleCacheOp = _fileSystem.BundleCache.WriteCacheAsync(options);
_bundleCacheOp.StartOperation();
AddChildOperation(_bundleCacheOp);
}
if (IsWaitForCompletion)
_bundleCacheOp.WaitForCompletion();
_bundleCacheOp.UpdateOperation();
if (_bundleCacheOp.IsDone == false)
return;
if (_bundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_bundleCacheOp.Error);
}
// 注意:缓存完成后直接删除临时文件
DeleteTempFile();
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
private void DeleteTempFile()
{
try
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
catch (System.Exception ex)
{
YooLogger.LogWarning($"Failed to delete temp file '{_tempFilePath}': {ex.Message}.");
}
}
}
}

View File

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

View File

@@ -0,0 +1,65 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载缓存包裹哈希文件操作
/// </summary>
internal sealed class LoadCachePackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadPackageHash,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _packageVersion;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { get; private set; }
internal LoadCachePackageHashOperation(SandboxFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
protected override void InternalStart()
{
_steps = ESteps.LoadPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadPackageHash)
{
string filePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
if (File.Exists(filePath) == false)
{
_steps = ESteps.Done;
SetError($"Could not find cache package hash file: '{filePath}'.");
return;
}
PackageHash = FileUtility.ReadAllText(filePath);
if (TextUtility.ValidateContent(PackageHash, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Cache package hash file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,115 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 加载缓存包裹清单文件操作
/// </summary>
internal sealed class LoadCachePackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private DeserializeManifestOperation _deserializeManifestOp;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { get; private set; }
internal LoadCachePackageManifestOperation(SandboxFileSystem fileSystem, string packageVersion, string packageHash)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_packageHash = packageHash;
}
protected override void InternalStart()
{
_steps = ESteps.LoadFileData;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadFileData)
{
string manifestFilePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
if (File.Exists(manifestFilePath))
{
try
{
_fileData = File.ReadAllBytes(manifestFilePath);
_steps = ESteps.VerifyFileData;
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
SetError($"Failed to read cache package manifest file: {ex.Message}.");
}
}
else
{
_steps = ESteps.Done;
SetError($"Could not find cache manifest file: '{manifestFilePath}'.");
}
}
if (_steps == ESteps.VerifyFileData)
{
if (PackageManifestHelper.VerifyManifestData(_fileData, _packageHash))
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
SetError("Failed to verify cache package manifest file.");
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializeManifestOp == null)
{
_deserializeManifestOp = new DeserializeManifestOperation(_fileSystem.ManifestDecryptor, _fileData);
_deserializeManifestOp.StartOperation();
AddChildOperation(_deserializeManifestOp);
}
_deserializeManifestOp.UpdateOperation();
Progress = _deserializeManifestOp.Progress;
if (_deserializeManifestOp.IsDone == false)
return;
if (_deserializeManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
Manifest = _deserializeManifestOp.Manifest;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_deserializeManifestOp.Error);
}
}
}
protected override string InternalGetDescription()
{
return $"PackageVersion: {_packageVersion} PackageHash: {_packageHash}";
}
}
}

View File

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

View File

@@ -0,0 +1,105 @@
namespace YooAsset
{
/// <summary>
/// 请求远端包裹版本操作
/// </summary>
internal sealed class RequestRemotePackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private IDownloadTextRequest _downloadTextRequest;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
internal string PackageVersion { get; set; }
internal RequestRemotePackageVersionOperation(SandboxFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
protected override void InternalStart()
{
_steps = ESteps.RequestPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_downloadTextRequest == null)
{
string fileName = YooAssetConfiguration.GetPackageVersionFileName(_fileSystem.PackageName);
string url = GetWebRequestUrl(fileName);
int watchDogTime = _fileSystem.DownloadWatchdogTimeout;
var args = new DownloadDataRequestArgs(
url: url,
timeout: _timeout,
watchdogTimeout: watchDogTime);
_downloadTextRequest = _fileSystem.DownloadBackend.CreateTextRequest(args);
_downloadTextRequest.SendRequest();
}
Progress = _downloadTextRequest.DownloadProgress;
if (_downloadTextRequest.IsDone == false)
return;
if (_downloadTextRequest.Status == EDownloadRequestStatus.Succeeded)
{
PackageVersion = _downloadTextRequest.Result;
if (TextUtility.ValidateContent(PackageVersion, out string validateError) == false)
{
_steps = ESteps.Done;
SetError($"Remote package version file validation failed: {validateError}.");
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
else
{
_steps = ESteps.Done;
SetError(_downloadTextRequest.Error);
_fileSystem.DownloadUrlPolicy.OnRequestFailed(_downloadTextRequest.Url, _downloadTextRequest.HttpCode, _downloadTextRequest.HttpError);
}
}
}
protected override void InternalDispose()
{
if (_downloadTextRequest != null)
{
_downloadTextRequest.Dispose();
_downloadTextRequest = null;
}
}
private string GetWebRequestUrl(string fileName)
{
var urls = _fileSystem.RemoteService.GetRemoteUrls(fileName);
string url = _fileSystem.DownloadUrlPolicy.SelectUrl(urls);
// 在URL末尾添加时间戳
if (_appendTimeTicks)
return $"{url}?{System.DateTime.UtcNow.Ticks}";
else
return url;
}
}
}

View File

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

View File

@@ -0,0 +1,215 @@
using System;
using System.IO;
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的清理缓存操作
/// </summary>
internal sealed class SFSClearCacheOperation : FSClearCacheOperation
{
private enum ESteps
{
None,
CheckReadOnly,
ClearCache,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly FSClearCacheOptions _options;
private BCClearCacheOperation _clearCacheOp;
private ESteps _steps = ESteps.None;
internal SFSClearCacheOperation(SandboxFileSystem fileSystem, FSClearCacheOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.CheckReadOnly;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckReadOnly)
{
if (_fileSystem.BundleCache.IsReadOnly)
{
_steps = ESteps.Done;
SetResult();
return;
}
_steps = ESteps.ClearCache;
}
if (_steps == ESteps.ClearCache)
{
if (_clearCacheOp == null)
{
_clearCacheOp = _fileSystem.BundleCache.ClearCacheAsync(_options.ConvertTo());
_clearCacheOp.StartOperation();
AddChildOperation(_clearCacheOp);
}
_clearCacheOp.UpdateOperation();
if (_clearCacheOp.IsDone == false)
return;
if (_clearCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_clearCacheOp.Error);
}
}
}
}
/// <summary>
/// 沙盒文件系统的清理所有缓存清单操作
/// </summary>
internal sealed class SFSClearAllCacheManifestOperation : FSClearCacheOperation
{
private enum ESteps
{
None,
ClearAllCacheFiles,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal SFSClearAllCacheManifestOperation(SandboxFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.ClearAllCacheFiles;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.ClearAllCacheFiles)
{
try
{
// TODO: 如果正在下载资源清单,会有几率触发异常!
string directoryRoot = _fileSystem.GetCacheManifestFilesRoot();
DirectoryInfo directoryInfo = new DirectoryInfo(directoryRoot);
if (directoryInfo.Exists)
{
foreach (FileInfo fileInfo in directoryInfo.GetFiles())
{
string fileName = fileInfo.Name;
if (fileName == SandboxFileSystemConsts.AppFootprintFileName)
continue;
fileInfo.Delete();
}
}
_steps = ESteps.Done;
SetResult();
}
catch (Exception ex)
{
_steps = ESteps.Done;
SetError(ex.Message);
}
}
}
}
/// <summary>
/// 沙盒文件系统的清理未使用缓存清单操作
/// </summary>
internal sealed class SFSClearUnusedCacheManifestOperation : FSClearCacheOperation
{
private enum ESteps
{
None,
CheckManifest,
ClearUnusedCacheFiles,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private ESteps _steps = ESteps.None;
internal SFSClearUnusedCacheManifestOperation(SandboxFileSystem fileSystem, PackageManifest manifest)
{
_fileSystem = fileSystem;
_manifest = manifest;
}
protected override void InternalStart()
{
_steps = ESteps.CheckManifest;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckManifest)
{
if (_manifest == null)
{
_steps = ESteps.Done;
SetError("Could not find active package manifest.");
}
else
{
_steps = ESteps.ClearUnusedCacheFiles;
}
}
if (_steps == ESteps.ClearUnusedCacheFiles)
{
try
{
string activeManifestFileName = YooAssetConfiguration.GetManifestBinaryFileName(_manifest.PackageName, _manifest.PackageVersion);
string activeHashFileName = YooAssetConfiguration.GetPackageHashFileName(_manifest.PackageName, _manifest.PackageVersion);
// TODO: 如果正在下载资源清单,会有几率触发异常!
string directoryRoot = _fileSystem.GetCacheManifestFilesRoot();
DirectoryInfo directoryInfo = new DirectoryInfo(directoryRoot);
if (directoryInfo.Exists)
{
foreach (FileInfo fileInfo in directoryInfo.GetFiles())
{
string fileName = fileInfo.Name;
if (fileName == SandboxFileSystemConsts.AppFootprintFileName)
continue;
if (fileName == activeManifestFileName || fileName == activeHashFileName)
continue;
fileInfo.Delete();
}
}
_steps = ESteps.Done;
SetResult();
}
catch (Exception ex)
{
_steps = ESteps.Done;
SetError(ex.Message);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,179 @@
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的下载文件操作
/// </summary>
internal sealed class SFSDownloadBundleOperation : FSDownloadBundleOperation
{
private enum ESteps
{
None,
CheckExists,
CreateDownload,
CheckDownload,
TryAgain,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly FSDownloadBundleOptions _options;
private readonly DownloadRetryController _downloadRetryController;
private DownloadFileBaseOperation _downloadFileOp;
private ESteps _steps = ESteps.None;
internal SFSDownloadBundleOperation(SandboxFileSystem fileSystem, FSDownloadBundleOptions options) : base(options.Bundle)
{
_fileSystem = fileSystem;
_options = options;
_downloadRetryController = new DownloadRetryController(options.RetryCount, _fileSystem.DownloadRetryPolicy);
}
protected override void InternalStart()
{
_steps = ESteps.CheckExists;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.BundleCache.IsCached(Bundle.BundleGuid))
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.CreateDownload;
}
}
// 创建下载器
if (_steps == ESteps.CreateDownload)
{
_downloadFileOp = _fileSystem.DownloadScheduler.TryGetDownloadOperation(Bundle);
if (_downloadFileOp == null)
{
if (string.IsNullOrEmpty(_options.ImportFilePath))
{
// 下载远端文件
string url = GetRequestUrl(Bundle.GetFileName());
_downloadFileOp = new DownloadAndCacheFileOperation(_fileSystem, Bundle, url);
_fileSystem.DownloadScheduler.RegisterDownloadOperation(_downloadFileOp);
}
else
{
// 导入本地文件
_downloadFileOp = new ImportAndCacheFileOperation(_fileSystem, Bundle, _options.ImportFilePath);
_fileSystem.DownloadScheduler.RegisterDownloadOperation(_downloadFileOp);
}
}
_steps = ESteps.CheckDownload;
}
// 检测结果
if (_steps == ESteps.CheckDownload)
{
if (IsWaitForCompletion)
{
if (_downloadFileOp is DownloadAndCacheFileOperation)
{
_steps = ESteps.Done;
SetError($"Attempting to load bundle '{Bundle.BundleName}' from remote: '{_downloadFileOp.Url}'.");
return;
}
_downloadFileOp.WaitForCompletion();
}
// 注意:不主动调用 _downloadFileOp.UpdateOperation()
// 说明:下载任务由 DownloadSchedulerOperation 统一驱动,此处仅读取状态。
// 说明:同步等待由 WaitForCompletion() 内部的 ExecuteBatch() 保证。
Progress = _downloadFileOp.Progress;
Report = _downloadFileOp.LatestReport;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeeded)
{
_fileSystem.DownloadUrlPolicy.OnRequestSucceeded(_downloadFileOp.Url);
_steps = ESteps.Done;
SetResult();
}
else
{
// 注意:本地导入失败时跳过重试策略
if (string.IsNullOrEmpty(_options.ImportFilePath) == false)
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
YooLogger.LogError(Error);
}
else
{
string url = _downloadFileOp.Url;
long httpCode = _downloadFileOp.LatestReport.HttpCode;
string httpError = _downloadFileOp.LatestReport.HttpError;
_fileSystem.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
YooLogger.LogError(Error);
}
}
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_downloadRetryController.TickRetryDelay())
{
if (_downloadFileOp != null)
{
_downloadFileOp.Release();
_downloadFileOp = null;
}
Progress = 0f;
Report = DownloadReport.Empty;
_steps = ESteps.CreateDownload;
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
protected override void InternalAbort()
{
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_downloadFileOp != null)
{
_downloadFileOp.Release();
}
}
}
/// <summary>
/// 获取网络请求地址
/// </summary>
private string GetRequestUrl(string fileName)
{
var urls = _fileSystem.RemoteService.GetRemoteUrls(fileName);
return _fileSystem.DownloadUrlPolicy.SelectUrl(urls);
}
}
}

View File

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

View File

@@ -0,0 +1,146 @@
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的初始化操作
/// </summary>
internal sealed class SFSInitializeOperation : FSInitializeOperation
{
private enum ESteps
{
None,
CheckPlatform,
CheckParameter,
CheckAppFootprint,
InitializeBundleCache,
CreateScheduler,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private BCInitializeOperation _initializeBundleCacheOp;
private ESteps _steps = ESteps.None;
internal SFSInitializeOperation(SandboxFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.CheckPlatform;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckPlatform)
{
#if UNITY_WEBGL
_steps = ESteps.Done;
SetError($"{nameof(SandboxFileSystem)} does not support the WebGL platform.");
#else
_steps = ESteps.CheckParameter;
#endif
}
if (_steps == ESteps.CheckParameter)
{
if (_fileSystem.RemoteService == null)
{
_steps = ESteps.Done;
SetError($"{nameof(IRemoteService)} is null.");
return;
}
_steps = ESteps.CheckAppFootprint;
}
if (_steps == ESteps.CheckAppFootprint)
{
string footprintFilePath = _fileSystem.GetSandboxAppFootprintFilePath();
var appFootprint = new ApplicationFootprint(footprintFilePath);
appFootprint.Load(_fileSystem.PackageName);
// 如果水印发生变化,则说明覆盖安装后首次打开游戏
if (appFootprint.IsDirty())
{
if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.None)
{
YooLogger.LogWarning("No action required on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllCacheFiles)
{
_fileSystem.DeleteAllBundleFiles();
_fileSystem.DeleteAllManifestFiles();
_fileSystem.DeleteAllTempFiles();
YooLogger.LogWarning("Deleted all cache files on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllBundleFiles)
{
_fileSystem.DeleteAllBundleFiles();
YooLogger.LogWarning("Deleted all bundle files on overwrite installation.");
}
else if (_fileSystem.InstallCleanupMode == EInstallCleanupMode.ClearAllManifestFiles)
{
_fileSystem.DeleteAllManifestFiles();
YooLogger.LogWarning("Deleted all manifest files on overwrite installation.");
}
else
{
throw new YooInternalException($"Unhandled {nameof(EInstallCleanupMode)} value: {_fileSystem.InstallCleanupMode}.");
}
appFootprint.Overwrite(_fileSystem.PackageName);
}
_steps = ESteps.InitializeBundleCache;
}
if (_steps == ESteps.InitializeBundleCache)
{
if (_initializeBundleCacheOp == null)
{
_initializeBundleCacheOp = _fileSystem.BundleCache.InitializeAsync();
_initializeBundleCacheOp.StartOperation();
AddChildOperation(_initializeBundleCacheOp);
}
_initializeBundleCacheOp.UpdateOperation();
Progress = _initializeBundleCacheOp.Progress;
if (_initializeBundleCacheOp.IsDone == false)
return;
if (_initializeBundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.CreateScheduler;
}
else
{
_steps = ESteps.Done;
SetError(_initializeBundleCacheOp.Error);
}
}
if (_steps == ESteps.CreateScheduler)
{
// 注意: 下载调度中心在最后一步创建,防止初始化失败后残留任务。
// 注意: 下载调度中心作为独立任务运行!
if (_fileSystem.DownloadScheduler == null)
{
var schedulerConfig = new DownloadSchedulerOperation.Configuration(
schedulerName: _fileSystem.GetType().Name,
downloadBackend: _fileSystem.DownloadBackend,
maxConcurrency: _fileSystem.DownloadMaxConcurrency,
maxRequestsPerFrame: _fileSystem.DownloadMaxRequestsPerFrame);
_fileSystem.DownloadScheduler = new DownloadSchedulerOperation(schedulerConfig);
AsyncOperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.DownloadScheduler);
}
_steps = ESteps.Done;
SetResult();
}
}
}
}

View File

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

View File

@@ -0,0 +1,161 @@
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的加载资源包操作
/// </summary>
internal sealed class SFSLoadPackageBundleOperation : FSLoadPackageBundleOperation
{
private enum ESteps
{
None,
Prepare,
DownloadFile,
AbortDownload,
LoadBundle,
CheckResult,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly FSLoadPackageBundleOptions _options;
private FSDownloadBundleOperation _downloadFileOp;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal SFSLoadPackageBundleOperation(SandboxFileSystem fileSystem, FSLoadPackageBundleOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.Prepare;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
if (_fileSystem.BundleCache.IsCached(_options.Bundle.BundleGuid))
{
_steps = ESteps.LoadBundle;
}
else
{
if (_fileSystem.DisableOnDemandDownload)
{
_steps = ESteps.Done;
SetError($"Bundle is not cached: '{_options.Bundle.BundleName}'.");
YooLogger.LogWarning(Error);
}
else
{
_steps = ESteps.DownloadFile;
}
}
}
if (_steps == ESteps.DownloadFile)
{
// 中断下载
if (ShouldAbortDownload)
{
if (_downloadFileOp != null)
_downloadFileOp.AbortOperation();
_steps = ESteps.AbortDownload;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
var options = new FSDownloadBundleOptions(_options.Bundle, int.MaxValue);
_downloadFileOp = _fileSystem.DownloadBundleAsync(options);
_downloadFileOp.StartOperation();
AddChildOperation(_downloadFileOp);
}
if (IsWaitForCompletion)
_downloadFileOp.WaitForCompletion();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadBundle;
}
else
{
_steps = ESteps.Done;
SetError(_downloadFileOp.Error);
}
}
if (_steps == ESteps.AbortDownload)
{
if (_downloadFileOp != null)
{
if (IsWaitForCompletion)
_downloadFileOp.WaitForCompletion();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
SetError("Bundle download aborted.");
}
if (_steps == ESteps.LoadBundle)
{
_loadBundleOp = _fileSystem.BundleCache.LoadBundleAsync(_options.ConvertTo());
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (IsWaitForCompletion)
_loadBundleOp.WaitForCompletion();
_loadBundleOp.UpdateOperation();
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
{
_steps = ESteps.Done;
SetError("Fatal error: loaded bundle handle is null.");
YooLogger.LogError(Error);
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
}
}

View File

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

View File

@@ -0,0 +1,166 @@
using System.IO;
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的加载包裹清单操作
/// </summary>
internal sealed class SFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
DownloadPackageHash,
DownloadPackageManifest,
LoadPackageHash,
LoadPackageManifest,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private DownloadPackageHashOperation _downloadPackageHashOp;
private DownloadPackageManifestOperation _downloadPackageManifestOp;
private LoadCachePackageHashOperation _loadCachePackageHashOp;
private LoadCachePackageManifestOperation _loadCachePackageManifestOp;
private ESteps _steps = ESteps.None;
internal SFSLoadPackageManifestOperation(SandboxFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
protected override void InternalStart()
{
_steps = ESteps.DownloadPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadPackageHash)
{
if (_downloadPackageHashOp == null)
{
_downloadPackageHashOp = new DownloadPackageHashOperation(_fileSystem, _packageVersion, _timeout);
_downloadPackageHashOp.StartOperation();
AddChildOperation(_downloadPackageHashOp);
}
_downloadPackageHashOp.UpdateOperation();
if (_downloadPackageHashOp.IsDone == false)
return;
if (_downloadPackageHashOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.DownloadPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_downloadPackageHashOp.Error);
}
}
if (_steps == ESteps.DownloadPackageManifest)
{
if (_downloadPackageManifestOp == null)
{
_downloadPackageManifestOp = new DownloadPackageManifestOperation(_fileSystem, _packageVersion, _timeout);
_downloadPackageManifestOp.StartOperation();
AddChildOperation(_downloadPackageManifestOp);
}
_downloadPackageManifestOp.UpdateOperation();
if (_downloadPackageManifestOp.IsDone == false)
return;
if (_downloadPackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadPackageHash;
}
else
{
_steps = ESteps.Done;
SetError(_downloadPackageManifestOp.Error);
}
}
if (_steps == ESteps.LoadPackageHash)
{
if (_loadCachePackageHashOp == null)
{
_loadCachePackageHashOp = new LoadCachePackageHashOperation(_fileSystem, _packageVersion);
_loadCachePackageHashOp.StartOperation();
AddChildOperation(_loadCachePackageHashOp);
}
_loadCachePackageHashOp.UpdateOperation();
if (_loadCachePackageHashOp.IsDone == false)
return;
if (_loadCachePackageHashOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_loadCachePackageHashOp.Error);
ClearCacheFatalFile();
}
}
if (_steps == ESteps.LoadPackageManifest)
{
if (_loadCachePackageManifestOp == null)
{
string packageHash = _loadCachePackageHashOp.PackageHash;
_loadCachePackageManifestOp = new LoadCachePackageManifestOperation(_fileSystem, _packageVersion, packageHash);
_loadCachePackageManifestOp.StartOperation();
AddChildOperation(_loadCachePackageManifestOp);
}
_loadCachePackageManifestOp.UpdateOperation();
Progress = _loadCachePackageManifestOp.Progress;
if (_loadCachePackageManifestOp.IsDone == false)
return;
if (_loadCachePackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
Manifest = _loadCachePackageManifestOp.Manifest;
}
else
{
_steps = ESteps.Done;
SetError(_loadCachePackageManifestOp.Error);
ClearCacheFatalFile();
}
}
}
private void ClearCacheFatalFile()
{
// 注意:如果加载沙盒内的清单报错,为了避免流程被卡住,主动把损坏的文件删除。
string manifestFilePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
if (File.Exists(manifestFilePath))
{
YooLogger.LogWarning($"Invalid package manifest file has been removed: '{manifestFilePath}'.");
File.Delete(manifestFilePath);
}
string hashFilePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
if (File.Exists(hashFilePath))
{
YooLogger.LogWarning($"Invalid package hash file has been removed: '{hashFilePath}'.");
File.Delete(hashFilePath);
}
}
}
}

View File

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

View File

@@ -0,0 +1,66 @@
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统的查询包裹版本操作
/// </summary>
internal sealed class SFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
GetPackageVersion,
Done,
}
private readonly SandboxFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private RequestRemotePackageVersionOperation _requestRemotePackageVersionOp;
private ESteps _steps = ESteps.None;
internal SFSRequestPackageVersionOperation(SandboxFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
protected override void InternalStart()
{
_steps = ESteps.GetPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetPackageVersion)
{
if (_requestRemotePackageVersionOp == null)
{
_requestRemotePackageVersionOp = new RequestRemotePackageVersionOperation(_fileSystem, _appendTimeTicks, _timeout);
_requestRemotePackageVersionOp.StartOperation();
AddChildOperation(_requestRemotePackageVersionOp);
}
_requestRemotePackageVersionOp.UpdateOperation();
Progress = _requestRemotePackageVersionOp.Progress;
if (_requestRemotePackageVersionOp.IsDone == false)
return;
if (_requestRemotePackageVersionOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
PackageVersion = _requestRemotePackageVersionOp.PackageVersion;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_requestRemotePackageVersionOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,476 @@
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统,管理沙盒目录下的资源文件存储与访问。
/// </summary>
internal class SandboxFileSystem : IFileSystem
{
/// <summary>
/// 临时文件路径映射表
/// </summary>
protected readonly Dictionary<string, string> _tempFilePathMapping = new Dictionary<string, string>(10000);
/// <summary>
/// 包裹根目录路径
/// </summary>
protected string _packageRoot;
/// <summary>
/// 临时文件根目录路径
/// </summary>
protected string _tempFilesRoot;
/// <summary>
/// 缓存清单文件根目录路径
/// </summary>
protected string _cacheManifestFilesRoot;
/// <summary>
/// 缓存资源包文件根目录路径
/// </summary>
protected string _cacheBundleFilesRoot;
/// <summary>
/// 沙盒 Bundle 缓存系统
/// </summary>
public IBundleCache BundleCache { get; private set; }
/// <summary>
/// 下载调度器
/// </summary>
public DownloadSchedulerOperation DownloadScheduler { get; internal set; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; private set; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { get; private set; }
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { get; private set; }
/// <summary>
/// 自定义参数:覆盖安装缓存清理模式
/// </summary>
public EInstallCleanupMode InstallCleanupMode { get; private set; } = EInstallCleanupMode.None;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验级别
/// </summary>
public EFileVerifyLevel FileVerifyLevel { get; private set; } = EFileVerifyLevel.Low;
/// <summary>
/// 自定义参数:初始化时缓存文件校验的最大并发数
/// </summary>
/// <remarks>
/// 默认值8推荐值为处理器数两倍。过大的值可能导致线程池任务过多影响系统稳定性。
/// </remarks>
public int FileVerifyMaxConcurrency { get; private set; } = 8;
/// <summary>
/// 自定义参数:禁用边玩边下机制
/// </summary>
public bool DisableOnDemandDownload { get; private set; } = false;
/// <summary>
/// 自定义参数:最大并发连接数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32。过大的并发数可能被服务器限流也会增加本地资源消耗。
/// </remarks>
public int DownloadMaxConcurrency { get; private set; } = 8;
/// <summary>
/// 自定义参数:每帧发起的最大请求数
/// </summary>
/// <remarks>
/// 默认值8推荐范围 1-32。避免单帧发起过多请求导致卡顿。
/// </remarks>
public int DownloadMaxRequestsPerFrame { get; private set; } = 8;
/// <summary>
/// 自定义参数:下载任务的看门狗机制超时时间
/// </summary>
public int DownloadWatchdogTimeout { get; private set; } = 0;
/// <summary>
/// 自定义参数:启用断点续传的最小尺寸
/// </summary>
public long ResumeDownloadMinimumSize { get; private set; } = long.MaxValue;
/// <summary>
/// 自定义参数:远程服务接口的实例类
/// </summary>
public IRemoteService RemoteService { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 解密器
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数RawBundle 解密器
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 备用解密器
/// </summary>
public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; private set; }
/// <summary>
/// 自定义参数:资源清单解密器
/// </summary>
public IManifestDecryptor ManifestDecryptor { get; private set; }
/// <summary>
/// 自定义参数:下载重试判定策略
/// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; private set; }
/// <summary>
/// 自定义参数URL 选择策略
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; private set; }
#endregion
/// <summary>
/// 创建实例
/// </summary>
public SandboxFileSystem()
{
}
/// <inheritdoc />
public FSInitializeOperation InitializeAsync()
{
var operation = new SFSInitializeOperation(this);
return operation;
}
/// <inheritdoc />
public FSRequestPackageVersionOperation RequestPackageVersionAsync(FSRequestPackageVersionOptions options)
{
var operation = new SFSRequestPackageVersionOperation(this, options.AppendTimeTicks, options.Timeout);
return operation;
}
/// <inheritdoc />
public FSLoadPackageManifestOperation LoadPackageManifestAsync(FSLoadPackageManifestOptions options)
{
var operation = new SFSLoadPackageManifestOperation(this, options.PackageVersion, options.Timeout);
return operation;
}
/// <inheritdoc />
public FSLoadPackageBundleOperation LoadPackageBundleAsync(FSLoadPackageBundleOptions options)
{
var operation = new SFSLoadPackageBundleOperation(this, options);
return operation;
}
/// <inheritdoc />
public FSDownloadBundleOperation DownloadBundleAsync(FSDownloadBundleOptions options)
{
var downloader = new SFSDownloadBundleOperation(this, options);
return downloader;
}
/// <inheritdoc />
public FSClearCacheOperation ClearCacheAsync(FSClearCacheOptions options)
{
if (options.ClearMethod == ClearCacheMethods.ClearAllManifestFiles)
{
var operation = new SFSClearAllCacheManifestOperation(this);
return operation;
}
else if (options.ClearMethod == ClearCacheMethods.ClearUnusedManifestFiles)
{
var operation = new SFSClearUnusedCacheManifestOperation(this, options.Manifest);
return operation;
}
else
{
var operation = new SFSClearCacheOperation(this, options);
return operation;
}
}
/// <inheritdoc />
public void SetParameter(string paramName, object value)
{
if (paramName == nameof(EFileSystemParameter.DownloadBackend))
{
DownloadBackend = FileSystemHelper.CastParameter<IDownloadBackend>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.UnityWebRequestCreator))
{
WebRequestCreator = FileSystemHelper.CastParameter<UnityWebRequestCreator>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.InstallCleanupMode))
{
InstallCleanupMode = FileSystemHelper.CastParameter<EInstallCleanupMode>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.FileVerifyLevel))
{
FileVerifyLevel = FileSystemHelper.CastParameter<EFileVerifyLevel>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.FileVerifyMaxConcurrency))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"FILE_VERIFY_MAX_CONCURRENCY value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
FileVerifyMaxConcurrency = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.DownloadDisableOndemand))
{
DisableOnDemandDownload = FileSystemHelper.CastParameter<bool>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxConcurrency))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_CONCURRENCY value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
DownloadMaxConcurrency = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.DownloadMaxRequestPerFrame))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
if (convertValue > 32)
{
YooLogger.LogWarning($"DOWNLOAD_MAX_REQUEST_PER_FRAME value {convertValue} is too large, clamped to 32. Recommended range: 1 - 32.");
}
// 限制在合理范围内1-32
DownloadMaxRequestsPerFrame = Mathf.Clamp(convertValue, 1, 32);
}
else if (paramName == nameof(EFileSystemParameter.DownloadWatchdogTimeout))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
DownloadWatchdogTimeout = Mathf.Max(convertValue, 0);
}
else if (paramName == nameof(EFileSystemParameter.DownloadResumeMinimumSize))
{
ResumeDownloadMinimumSize = FileSystemHelper.CastParameter<long>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RemoteService))
{
RemoteService = FileSystemHelper.CastParameter<IRemoteService>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleFallbackDecryptor))
{
AssetBundleFallbackDecryptor = FileSystemHelper.CastParameter<IBundleMemoryDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.ManifestDecryptor))
{
ManifestDecryptor = FileSystemHelper.CastParameter<IManifestDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadRetryPolicy))
{
DownloadRetryPolicy = FileSystemHelper.CastParameter<IDownloadRetryPolicy>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadUrlPolicy))
{
DownloadUrlPolicy = FileSystemHelper.CastParameter<IDownloadUrlPolicy>(paramName, value);
}
else
{
throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName));
}
}
/// <inheritdoc />
public void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
_packageRoot = GetDefaultCachePackageRoot(packageName);
else
_packageRoot = packageRoot;
_cacheBundleFilesRoot = PathUtility.Combine(_packageRoot, SandboxFileSystemConsts.BundleFilesFolderName);
_cacheManifestFilesRoot = PathUtility.Combine(_packageRoot, SandboxFileSystemConsts.ManifestFilesFolderName);
_tempFilesRoot = PathUtility.Combine(_packageRoot, SandboxFileSystemConsts.TempFilesFolderName);
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
// 创建默认的下载重试策略
if (DownloadRetryPolicy == null)
DownloadRetryPolicy = new DefaultDownloadRetryPolicy();
// 创建默认的 URL 选择策略
if (DownloadUrlPolicy == null)
DownloadUrlPolicy = new DefaultDownloadUrlPolicy();
// 创建文件缓存系统
{
var cacheConfig = new SandboxBundleCache.Configuration(
fileVerifyMaxConcurrency: FileVerifyMaxConcurrency,
fileVerifyLevel: FileVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
assetBundleFallbackDecryptor: AssetBundleFallbackDecryptor);
BundleCache = new SandboxBundleCache(PackageName, _cacheBundleFilesRoot, cacheConfig);
}
}
/// <inheritdoc />
public void OnDestroy()
{
if (BundleCache != null)
{
BundleCache.Dispose();
BundleCache = null;
}
if (DownloadScheduler != null)
{
DownloadScheduler.AbortOperation();
DownloadScheduler = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
/// <inheritdoc />
public bool CanAcceptBundle(PackageBundle bundle)
{
// 注意:保底加载!
return true;
}
/// <inheritdoc />
public bool IsDownloadRequired(PackageBundle bundle)
{
return BundleCache.IsCached(bundle.BundleGuid) == false;
}
/// <inheritdoc />
public bool IsUnpackRequired(PackageBundle bundle)
{
return false;
}
/// <inheritdoc />
public bool IsImportRequired(PackageBundle bundle)
{
return BundleCache.IsCached(bundle.BundleGuid) == false;
}
#region
/// <summary>
/// 获取默认的缓存包裹根目录
/// </summary>
public string GetDefaultCachePackageRoot(string packageName)
{
string rootDirectory = YooAssetConfiguration.GetDefaultCacheRoot();
return PathUtility.Combine(rootDirectory, packageName);
}
/// <summary>
/// 获取缓存清单文件的根目录
/// </summary>
public string GetCacheManifestFilesRoot()
{
return _cacheManifestFilesRoot;
}
/// <summary>
/// 获取缓存包裹哈希文件路径
/// </summary>
public string GetCachePackageHashFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_cacheManifestFilesRoot, fileName);
}
/// <summary>
/// 获取缓存包裹清单文件路径
/// </summary>
public string GetCachePackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetConfiguration.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_cacheManifestFilesRoot, fileName);
}
/// <summary>
/// 获取沙盒应用程序水印文件路径
/// </summary>
public string GetSandboxAppFootprintFilePath()
{
return PathUtility.Combine(_cacheManifestFilesRoot, SandboxFileSystemConsts.AppFootprintFileName);
}
/// <summary>
/// 获取临时文件路径
/// </summary>
public string GetTempFilePath(PackageBundle bundle)
{
if (_tempFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
{
filePath = PathUtility.Combine(_tempFilesRoot, bundle.BundleGuid);
_tempFilePathMapping.Add(bundle.BundleGuid, filePath);
}
return filePath;
}
/// <summary>
/// 删除所有缓存的资源文件
/// </summary>
public void DeleteAllBundleFiles()
{
if (Directory.Exists(_cacheBundleFilesRoot))
{
Directory.Delete(_cacheBundleFilesRoot, true);
}
}
/// <summary>
/// 删除所有缓存的清单文件
/// </summary>
public void DeleteAllManifestFiles()
{
if (Directory.Exists(_cacheManifestFilesRoot))
{
Directory.Delete(_cacheManifestFilesRoot, true);
}
}
/// <summary>
/// 删除所有缓存的临时文件
/// </summary>
public void DeleteAllTempFiles()
{
if (Directory.Exists(_tempFilesRoot))
{
Directory.Delete(_tempFilesRoot, true);
}
}
#endregion
}
}

View File

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

View File

@@ -0,0 +1,29 @@
namespace YooAsset
{
/// <summary>
/// 沙盒文件系统常量定义
/// </summary>
internal static class SandboxFileSystemConsts
{
/// <summary>
/// 记录应用程序版本的文件名称
/// </summary>
public const string AppFootprintFileName = "ApplicationFootprint.bytes";
/// <summary>
/// 清单文件的文件夹名称
/// </summary>
public const string ManifestFilesFolderName = "ManifestFiles";
/// <summary>
/// 资源文件的文件夹名称
/// </summary>
public const string BundleFilesFolderName = "BundleFiles";
/// <summary>
/// 临时文件的文件夹名称
/// </summary>
public const string TempFilesFolderName = "TempFiles";
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,96 @@
namespace YooAsset
{
/// <summary>
/// WebGL 游戏平台文件系统初始化操作
/// </summary>
internal sealed class WGFSInitializeOperation : FSInitializeOperation
{
private enum ESteps
{
None,
CheckPlatform,
CheckParameter,
InitializeBundleCache,
Done,
}
private readonly WebGameFileSystem _fileSystem;
private BCInitializeOperation _initializeBundleCacheOp;
private ESteps _steps = ESteps.None;
internal WGFSInitializeOperation(WebGameFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
protected override void InternalStart()
{
_steps = ESteps.CheckPlatform;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckPlatform)
{
#if !UNITY_WEBGL
_steps = ESteps.Done;
SetError($"{nameof(WebGameFileSystem)} only supports the WebGL platform.");
#else
_steps = ESteps.CheckParameter;
#endif
}
if (_steps == ESteps.CheckParameter)
{
if (_fileSystem.RemoteService == null)
{
_steps = ESteps.Done;
SetError($"{nameof(IRemoteService)} is null.");
return;
}
// 检查URL双斜杠
// 注意:双斜杠会导致某小游戏平台加载文件失败,但网络请求又不返回失败!
var testUrls = _fileSystem.RemoteService.GetRemoteUrls("test.bundle");
foreach (var url in testUrls)
{
if (PathUtility.ContainsDoubleSlashes(url))
{
_steps = ESteps.Done;
SetError($"{nameof(IRemoteService)} returned URL contains double slashes: '{url}'.");
return;
}
}
_steps = ESteps.InitializeBundleCache;
}
if (_steps == ESteps.InitializeBundleCache)
{
if (_initializeBundleCacheOp == null)
{
_initializeBundleCacheOp = _fileSystem.BundleCache.InitializeAsync();
_initializeBundleCacheOp.StartOperation();
AddChildOperation(_initializeBundleCacheOp);
}
_initializeBundleCacheOp.UpdateOperation();
Progress = _initializeBundleCacheOp.Progress;
if (_initializeBundleCacheOp.IsDone == false)
return;
if (_initializeBundleCacheOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_initializeBundleCacheOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,79 @@
namespace YooAsset
{
/// <summary>
/// WebGL 游戏平台加载资源包操作
/// </summary>
internal sealed class WGFSLoadPackageBundleOperation : FSLoadPackageBundleOperation
{
private enum ESteps
{
None,
LoadBundle,
Done,
}
private readonly WebGameFileSystem _fileSystem;
private readonly FSLoadPackageBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal WGFSLoadPackageBundleOperation(WebGameFileSystem fileSystem, FSLoadPackageBundleOptions options)
{
_fileSystem = fileSystem;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.LoadBundle;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBundle)
{
if (_loadBundleOp == null)
{
_loadBundleOp = _fileSystem.BundleCache.LoadBundleAsync(_options.ConvertTo());
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
}
_loadBundleOp.UpdateOperation();
Progress = _loadBundleOp.Progress;
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
{
_steps = ESteps.Done;
SetError("Fatal error: loaded bundle handle is null.");
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
}
}
}
protected override void InternalWaitForCompletion()
{
if (_steps != ESteps.Done)
{
_steps = ESteps.Done;
SetError("WebGL platform does not support synchronous loading.");
YooLogger.LogError(Error);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More