yoo2.2,9
This commit is contained in:
Alex-Rachel
2025-01-25 13:46:17 +08:00
parent 634a392a1f
commit 6166fd24c6
584 changed files with 15684 additions and 11714 deletions

View File

@@ -0,0 +1,98 @@

namespace YooAsset
{
internal class DCFSInitializeOperation : FSInitializeFileSystemOperation
{
private enum ESteps
{
None,
CheckAppFootPrint,
SearchCacheFiles,
VerifyCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private SearchCacheFilesOperation _searchCacheFilesOp;
private VerifyCacheFilesOperation _verifyCacheFilesOp;
private ESteps _steps = ESteps.None;
internal DCFSInitializeOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
#if UNITY_WEBGL
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{nameof(DefaultCacheFileSystem)} is not support WEBGL platform !";
#else
_steps = ESteps.CheckAppFootPrint;
#endif
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckAppFootPrint)
{
var appFootPrint = new ApplicationFootPrint(_fileSystem);
appFootPrint.Load(_fileSystem.PackageName);
// 如果水印发生变化,则说明覆盖安装后首次打开游戏
if (appFootPrint.IsDirty())
{
_fileSystem.DeleteAllManifestFiles();
appFootPrint.Coverage(_fileSystem.PackageName);
YooLogger.Warning("Delete manifest files when application foot print dirty !");
}
_steps = ESteps.SearchCacheFiles;
}
if (_steps == ESteps.SearchCacheFiles)
{
if (_searchCacheFilesOp == null)
{
_searchCacheFilesOp = new SearchCacheFilesOperation(_fileSystem);
OperationSystem.StartOperation(_fileSystem.PackageName, _searchCacheFilesOp);
}
Progress = _searchCacheFilesOp.Progress;
if (_searchCacheFilesOp.IsDone == false)
return;
_steps = ESteps.VerifyCacheFiles;
}
if (_steps == ESteps.VerifyCacheFiles)
{
if (_verifyCacheFilesOp == null)
{
_verifyCacheFilesOp = new VerifyCacheFilesOperation(_fileSystem, _searchCacheFilesOp.Result);
OperationSystem.StartOperation(_fileSystem.PackageName, _verifyCacheFilesOp);
}
Progress = _verifyCacheFilesOp.Progress;
if (_verifyCacheFilesOp.IsDone == false)
return;
if (_verifyCacheFilesOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
YooLogger.Log($"Package '{_fileSystem.PackageName}' cached files count : {_fileSystem.FileCount}");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyCacheFilesOp.Error;
}
}
}
}
}

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,347 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
internal class DCFSLoadAssetBundleOperation : FSLoadBundleOperation
{
protected enum ESteps
{
None,
CheckExist,
DownloadFile,
LoadAssetBundle,
CheckResult,
Done,
}
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly PackageBundle _bundle;
protected FSDownloadFileOperation _downloadFileOp;
protected AssetBundleCreateRequest _createRequest;
private AssetBundle _assetBundle;
private Stream _managedStream;
protected ESteps _steps = ESteps.None;
internal DCFSLoadAssetBundleOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalOnStart()
{
_steps = ESteps.CheckExist;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExist)
{
if (_fileSystem.Exists(_bundle))
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.LoadAssetBundle;
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
DownloadParam downloadParam = new DownloadParam(int.MaxValue, 60);
_downloadFileOp = _fileSystem.DownloadFileAsync(_bundle, downloadParam);
}
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
DownloadProgress = _downloadFileOp.DownloadProgress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadAssetBundle;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadFileOp.Error;
}
}
if (_steps == ESteps.LoadAssetBundle)
{
if (_bundle.Encrypted)
{
if (_fileSystem.DecryptionServices == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"The {nameof(IDecryptionServices)} is null !";
YooLogger.Error(Error);
return;
}
}
if (IsWaitForAsyncComplete)
{
if (_bundle.Encrypted)
{
var decryptResult = _fileSystem.LoadEncryptedAssetBundle(_bundle);
_assetBundle = decryptResult.Result;
_managedStream = decryptResult.ManagedStream;
}
else
{
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
_assetBundle = AssetBundle.LoadFromFile(filePath);
}
}
else
{
if (_bundle.Encrypted)
{
var decryptResult = _fileSystem.LoadEncryptedAssetBundleAsync(_bundle);
_createRequest = decryptResult.CreateRequest;
_managedStream = decryptResult.ManagedStream;
}
else
{
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
_createRequest = AssetBundle.LoadFromFileAsync(filePath);
}
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_createRequest != null)
{
if (IsWaitForAsyncComplete)
{
// 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Suspend the main thread to load unity bundle.");
_assetBundle = _createRequest.assetBundle;
}
else
{
if (_createRequest.isDone == false)
return;
_assetBundle = _createRequest.assetBundle;
}
}
if (_assetBundle != null)
{
_steps = ESteps.Done;
Result = new AssetBundleResult(_fileSystem, _bundle, _assetBundle, _managedStream);
Status = EOperationStatus.Succeed;
return;
}
// 注意当缓存文件的校验等级为Low的时候并不能保证缓存文件的完整性。
// 说明在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性
EFileVerifyResult verifyResult = _fileSystem.VerifyCacheFile(_bundle);
if (verifyResult == EFileVerifyResult.Succeed)
{
if (_bundle.Encrypted)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to load encrypted asset bundle file : {_bundle.BundleName}";
YooLogger.Error(Error);
return;
}
// 注意:在安卓移动平台,华为和三星真机上有极小概率加载资源包失败。
// 说明:大多数情况在首次安装下载资源到沙盒内,游戏过程中切换到后台再回到游戏内有很大概率触发!
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
byte[] fileData = FileUtility.ReadAllBytes(filePath);
if (fileData != null && fileData.Length > 0)
{
_assetBundle = AssetBundle.LoadFromMemory(fileData);
if (_assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to load asset bundle from memory : {_bundle.BundleName}";
YooLogger.Error(Error);
}
else
{
_steps = ESteps.Done;
Result = new AssetBundleResult(_fileSystem, _bundle, _assetBundle, null);
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to read asset bundle file bytes : {_bundle.BundleName}";
YooLogger.Error(Error);
}
}
else
{
_steps = ESteps.Done;
_fileSystem.DeleteCacheBundleFile(_bundle.BundleGUID);
Status = EOperationStatus.Failed;
Error = $"Find corrupted asset bundle file and delete : {_bundle.BundleName}";
YooLogger.Error(Error);
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
if (_downloadFileOp != null && _downloadFileOp.Status == EOperationStatus.Failed)
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote !");
_steps = ESteps.Done;
break;
}
}
}
public override void AbortDownloadOperation()
{
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp != null)
_downloadFileOp.SetAbort();
}
}
}
internal class DCFSLoadRawBundleOperation : FSLoadBundleOperation
{
protected enum ESteps
{
None,
CheckExist,
DownloadFile,
LoadCacheRawBundle,
Done,
}
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly PackageBundle _bundle;
protected FSDownloadFileOperation _downloadFileOp;
protected ESteps _steps = ESteps.None;
internal DCFSLoadRawBundleOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalOnStart()
{
_steps = ESteps.CheckExist;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExist)
{
if (_fileSystem.Exists(_bundle))
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.LoadCacheRawBundle;
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
DownloadParam downloadParam = new DownloadParam(int.MaxValue, 60);
_downloadFileOp = _fileSystem.DownloadFileAsync(_bundle, downloadParam);
}
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
DownloadProgress = _downloadFileOp.DownloadProgress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadCacheRawBundle;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadFileOp.Error;
}
}
if (_steps == ESteps.LoadCacheRawBundle)
{
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
if (File.Exists(filePath))
{
_steps = ESteps.Done;
Result = new RawBundleResult(_fileSystem, _bundle);
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Can not found cache raw bundle file : {filePath}";
YooLogger.Error(Error);
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
//TODO 拷贝本地文件失败也会触发该错误!
if (_downloadFileOp != null && _downloadFileOp.Status == EOperationStatus.Failed)
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote !");
_steps = ESteps.Done;
break;
}
}
}
public override void AbortDownloadOperation()
{
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp != null)
_downloadFileOp.SetAbort();
}
}
}
}

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,158 @@
using System.IO;
namespace YooAsset
{
internal class DCFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
DownloadPackageHash,
DownloadPackageManifest,
LoadCachePackageHash,
LoadCachePackageManifest,
Done,
}
private readonly DefaultCacheFileSystem _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 DCFSLoadPackageManifestOperation(DefaultCacheFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_steps = ESteps.DownloadPackageHash;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadPackageHash)
{
if (_downloadPackageHashOp == null)
{
_downloadPackageHashOp = new DownloadPackageHashOperation(_fileSystem, _packageVersion, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _downloadPackageHashOp);
}
if (_downloadPackageHashOp.IsDone == false)
return;
if (_downloadPackageHashOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.DownloadPackageManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadPackageHashOp.Error;
}
}
if (_steps == ESteps.DownloadPackageManifest)
{
if (_downloadPackageManifestOp == null)
{
_downloadPackageManifestOp = new DownloadPackageManifestOperation(_fileSystem, _packageVersion, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _downloadPackageManifestOp);
}
if (_downloadPackageManifestOp.IsDone == false)
return;
if (_downloadPackageManifestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadCachePackageHash;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadPackageManifestOp.Error;
}
}
if (_steps == ESteps.LoadCachePackageHash)
{
if (_loadCachePackageHashOp == null)
{
_loadCachePackageHashOp = new LoadCachePackageHashOperation(_fileSystem, _packageVersion);
OperationSystem.StartOperation(_fileSystem.PackageName, _loadCachePackageHashOp);
}
if (_loadCachePackageHashOp.IsDone == false)
return;
if (_loadCachePackageHashOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadCachePackageManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadCachePackageHashOp.Error;
ClearCacheFatalFile();
}
}
if (_steps == ESteps.LoadCachePackageManifest)
{
if (_loadCachePackageManifestOp == null)
{
string packageHash = _loadCachePackageHashOp.PackageHash;
_loadCachePackageManifestOp = new LoadCachePackageManifestOperation(_fileSystem, _packageVersion, packageHash);
OperationSystem.StartOperation(_fileSystem.PackageName, _loadCachePackageManifestOp);
}
Progress = _loadCachePackageManifestOp.Progress;
if (_loadCachePackageManifestOp.IsDone == false)
return;
if (_loadCachePackageManifestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Manifest = _loadCachePackageManifestOp.Manifest;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadCachePackageManifestOp.Error;
ClearCacheFatalFile();
}
}
}
private void ClearCacheFatalFile()
{
// 注意:如果加载沙盒内的清单报错,为了避免流程被卡住,主动把损坏的文件删除。
string manifestFilePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
if (File.Exists(manifestFilePath))
{
YooLogger.Warning($"Invalid cache manifest file have been removed : {manifestFilePath}");
File.Delete(manifestFilePath);
}
string hashFilePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
if (File.Exists(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,62 @@

namespace YooAsset
{
internal class DCFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
GetPackageVersion,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private RequestRemotePackageVersionOperation _requestRemotePackageVersionOp;
private ESteps _steps = ESteps.None;
internal DCFSRequestPackageVersionOperation(DefaultCacheFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_steps = ESteps.GetPackageVersion;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetPackageVersion)
{
if (_requestRemotePackageVersionOp == null)
{
_requestRemotePackageVersionOp = new RequestRemotePackageVersionOperation(_fileSystem, _appendTimeTicks, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _requestRemotePackageVersionOp);
}
Progress = _requestRemotePackageVersionOp.Progress;
if (_requestRemotePackageVersionOp.IsDone == false)
return;
if (_requestRemotePackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
PackageVersion = _requestRemotePackageVersionOp.PackageVersion;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _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,8 @@
fileFormatVersion: 2
guid: 3414bfb159816a74f917270b7d48c25f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal sealed class ClearAllCacheBundleFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
GetAllCacheFiles,
ClearAllCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private List<string> _allBundleGUIDs;
private int _fileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearAllCacheBundleFilesOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_steps = ESteps.GetAllCacheFiles;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetAllCacheFiles)
{
_allBundleGUIDs = _fileSystem.GetAllCachedBundleGUIDs();
_fileTotalCount = _allBundleGUIDs.Count;
_steps = ESteps.ClearAllCacheFiles;
YooLogger.Log($"Found all cache files count : {_fileTotalCount}");
}
if (_steps == ESteps.ClearAllCacheFiles)
{
for (int i = _allBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _allBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_allBundleGUIDs.RemoveAt(i);
if (OperationSystem.IsBusy)
break;
}
if (_fileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_allBundleGUIDs.Count / _fileTotalCount);
if (_allBundleGUIDs.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,63 @@
using System;
using System.IO;
namespace YooAsset
{
internal sealed class ClearAllCacheManifestFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
ClearAllCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private ESteps _steps = ESteps.None;
internal ClearAllCacheManifestFilesOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_steps = ESteps.ClearAllCacheFiles;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.ClearAllCacheFiles)
{
try
{
// 注意:如果正在下载资源清单,会有几率触发异常!
string directoryRoot = _fileSystem.GetCacheManifestFilesRoot();
DirectoryInfo directoryInfo = new DirectoryInfo(directoryRoot);
if (directoryInfo.Exists)
{
foreach (FileInfo fileInfo in directoryInfo.GetFiles())
{
string fileName = fileInfo.Name;
if (fileName == DefaultCacheFileSystemDefine.AppFootPrintFileName)
continue;
fileInfo.Delete();
}
}
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
catch (Exception ex)
{
_steps = ESteps.Done;
Error = ex.Message;
Status = EOperationStatus.Failed;
}
}
}
}
}

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,120 @@
using System.Collections.Generic;
namespace YooAsset
{
internal class ClearCacheBundleFilesByTagsOperaiton : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
CheckArgs,
GetTagsCacheFiles,
ClearTagsCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private readonly object _clearParam;
private string[] _tags;
private List<string> _clearBundleGUIDs;
private int _clearFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearCacheBundleFilesByTagsOperaiton(DefaultCacheFileSystem fileSystem, PackageManifest manifest, object clearParam)
{
_fileSystem = fileSystem;
_manifest = manifest;
_clearParam = clearParam;
}
internal override void InternalOnStart()
{
_steps = ESteps.CheckArgs;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckArgs)
{
if (_clearParam == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Clear param is null !";
return;
}
if (_clearParam is string)
{
_tags = new string[] { _clearParam as string };
}
else if (_clearParam is List<string>)
{
var tempList = _clearParam as List<string>;
_tags = tempList.ToArray();
}
else if (_clearParam is string[])
{
_tags = _clearParam as string[];
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Invalid clear param : {_clearParam.GetType().FullName}";
return;
}
_steps = ESteps.GetTagsCacheFiles;
}
if (_steps == ESteps.GetTagsCacheFiles)
{
_clearBundleGUIDs = GetTagsBundleGUIDs();
_clearFileTotalCount = _clearBundleGUIDs.Count;
_steps = ESteps.ClearTagsCacheFiles;
}
if (_steps == ESteps.ClearTagsCacheFiles)
{
for (int i = _clearBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _clearBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_clearBundleGUIDs.RemoveAt(i);
if (OperationSystem.IsBusy)
break;
}
if (_clearFileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_clearBundleGUIDs.Count / _clearFileTotalCount);
if (_clearBundleGUIDs.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
private List<string> GetTagsBundleGUIDs()
{
var allBundleGUIDs = _fileSystem.GetAllCachedBundleGUIDs();
List<string> result = new List<string>(allBundleGUIDs.Count);
foreach (var bundleGUID in allBundleGUIDs)
{
if (_manifest.TryGetPackageBundleByBundleGUID(bundleGUID, out PackageBundle bundle))
{
if (bundle.HasTag(_tags))
{
result.Add(bundleGUID);
}
}
}
return result;
}
}
}

View File

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

View File

@@ -0,0 +1,83 @@
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal sealed class ClearUnusedCacheBundleFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
GetUnusedCacheFiles,
ClearUnusedCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private List<string> _unusedBundleGUIDs;
private int _unusedFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearUnusedCacheBundleFilesOperation(DefaultCacheFileSystem fileSystem, PackageManifest manifest)
{
_fileSystem = fileSystem;
_manifest = manifest;
}
internal override void InternalOnStart()
{
_steps = ESteps.GetUnusedCacheFiles;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetUnusedCacheFiles)
{
_unusedBundleGUIDs = GetUnusedBundleGUIDs();
_unusedFileTotalCount = _unusedBundleGUIDs.Count;
_steps = ESteps.ClearUnusedCacheFiles;
YooLogger.Log($"Found unused cache files count : {_unusedFileTotalCount}");
}
if (_steps == ESteps.ClearUnusedCacheFiles)
{
for (int i = _unusedBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _unusedBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_unusedBundleGUIDs.RemoveAt(i);
if (OperationSystem.IsBusy)
break;
}
if (_unusedFileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_unusedBundleGUIDs.Count / _unusedFileTotalCount);
if (_unusedBundleGUIDs.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
private List<string> GetUnusedBundleGUIDs()
{
var allBundleGUIDs = _fileSystem.GetAllCachedBundleGUIDs();
List<string> result = new List<string>(allBundleGUIDs.Count);
foreach (var bundleGUID in allBundleGUIDs)
{
if (_manifest.IsIncludeBundleFile(bundleGUID) == false)
{
result.Add(bundleGUID);
}
}
return result;
}
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using System;
using System.IO;
namespace YooAsset
{
internal sealed class ClearUnusedCacheManifestFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
ClearUnusedCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private ESteps _steps = ESteps.None;
internal ClearUnusedCacheManifestFilesOperation(DefaultCacheFileSystem fileSystem, PackageManifest manifest)
{
_fileSystem = fileSystem;
_manifest = manifest;
}
internal override void InternalOnStart()
{
_steps = ESteps.ClearUnusedCacheFiles;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.ClearUnusedCacheFiles)
{
try
{
string activeManifestFileName = YooAssetSettingsData.GetManifestBinaryFileName(_manifest.PackageName, _manifest.PackageVersion);
string activeHashFileName = YooAssetSettingsData.GetPackageHashFileName(_manifest.PackageName, _manifest.PackageVersion);
// 注意:如果正在下载资源清单,会有几率触发异常!
string directoryRoot = _fileSystem.GetCacheManifestFilesRoot();
DirectoryInfo directoryInfo = new DirectoryInfo(directoryRoot);
if (directoryInfo.Exists)
{
foreach (FileInfo fileInfo in directoryInfo.GetFiles())
{
string fileName = fileInfo.Name;
if (fileName == DefaultCacheFileSystemDefine.AppFootPrintFileName)
continue;
if (fileName == activeManifestFileName || fileName == activeHashFileName)
continue;
fileInfo.Delete();
}
}
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
catch (Exception ex)
{
_steps = ESteps.Done;
Error = ex.Message;
Status = EOperationStatus.Failed;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,87 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 支持Unity2018版本的断点续传下载器
/// </summary>
internal class DownloadHandlerFileRange : DownloadHandlerScript
{
private string _fileSavePath;
private long _fileTotalSize;
private UnityWebRequest _webRequest;
private FileStream _fileStream;
private long _localFileSize = 0;
private long _curFileSize = 0;
public DownloadHandlerFileRange(string fileSavePath, long fileTotalSize, UnityWebRequest webRequest) : base(new byte[1024 * 1024])
{
_fileSavePath = fileSavePath;
_fileTotalSize = fileTotalSize;
_webRequest = webRequest;
if (File.Exists(fileSavePath))
{
FileInfo fileInfo = new FileInfo(fileSavePath);
_localFileSize = fileInfo.Length;
}
_fileStream = new FileStream(_fileSavePath, FileMode.Append, FileAccess.Write);
_curFileSize = _localFileSize;
}
protected override bool ReceiveData(byte[] data, int dataLength)
{
if (data == null || dataLength == 0 || _webRequest.responseCode >= 400)
return false;
if (_fileStream == null)
return false;
_fileStream.Write(data, 0, dataLength);
_curFileSize += dataLength;
return true;
}
/// <summary>
/// UnityWebRequest.downloadHandler.data
/// </summary>
protected override byte[] GetData()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadHandler.text
/// </summary>
protected override string GetText()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadProgress
/// </summary>
protected override float GetProgress()
{
return _fileTotalSize == 0 ? 0 : ((float)_curFileSize) / _fileTotalSize;
}
/// <summary>
/// 释放下载句柄
/// </summary>
public void Cleanup()
{
if (_fileStream != null)
{
_fileStream.Flush();
_fileStream.Dispose();
_fileStream = null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,212 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class DownloadNormalFileOperation : DefaultDownloadFileOperation
{
private readonly DefaultCacheFileSystem _fileSystem;
private VerifyTempFileOperation _verifyOperation;
private bool _isReuqestLocalFile;
private string _tempFilePath;
private ESteps _steps = ESteps.None;
internal DownloadNormalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_isReuqestLocalFile = DownloadSystemHelper.IsRequestLocalFile(Param.MainURL);
_tempFilePath = _fileSystem.GetTempFilePath(Bundle);
_steps = ESteps.CheckExists;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.CreateRequest;
}
}
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
FileUtility.CreateFileDirectory(_tempFilePath);
// 删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
// 重置请求
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
_steps = ESteps.VerifyTempFile;
else
_steps = ESteps.TryAgain;
// 注意:最终释放请求器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
var element = new TempFileElement(_tempFilePath, Bundle.FileCRC, Bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
OperationSystem.StartOperation(_fileSystem.PackageName, _verifyOperation);
_steps = ESteps.CheckVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.CheckVerifyTempFile)
{
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
if (_fileSystem.WriteCacheBundleFile(Bundle, _tempFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
YooLogger.Error(Error);
}
}
else
{
_steps = ESteps.TryAgain;
Error = _verifyOperation.Error;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
//TODO 拷贝本地文件失败后不再尝试!
if (_isReuqestLocalFile)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalOnAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
}
internal override void InternalWaitForAsyncComplete()
{
//TODO 防止下载器挂起陷入无限死循环!
if (_steps == ESteps.None)
{
InternalOnStart();
}
while (true)
{
//TODO 如果是导入或解压本地文件,执行等待完毕
if (_isReuqestLocalFile)
{
InternalOnUpdate();
if (IsDone)
break;
}
else
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
private void CreateWebRequest()
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,91 @@
using System.IO;
namespace YooAsset
{
internal class DownloadPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckExist,
DownloadFile,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebFileRequestOperation _webFileRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
internal DownloadPackageHashOperation(DefaultCacheFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(DownloadPackageHashOperation));
_steps = ESteps.CheckExist;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExist)
{
string filePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
if (File.Exists(filePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_webFileRequestOp == null)
{
string savePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
string fileName = YooAssetSettingsData.GetPackageHashFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetWebRequestURL(fileName);
_webFileRequestOp = new UnityWebFileRequestOperation(webURL, savePath, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _webFileRequestOp);
}
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webFileRequestOp.Error;
WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(DownloadPackageHashOperation));
}
}
}
private string GetWebRequestURL(string fileName)
{
// 轮流返回请求地址
if (_requestCount % 2 == 0)
return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
}
}
}

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,91 @@
using System.IO;
namespace YooAsset
{
internal class DownloadPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckExist,
DownloadFile,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebFileRequestOperation _webFileRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
internal DownloadPackageManifestOperation(DefaultCacheFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(DownloadPackageManifestOperation));
_steps = ESteps.CheckExist;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExist)
{
string filePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
if (File.Exists(filePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_webFileRequestOp == null)
{
string savePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetDownloadRequestURL(fileName);
_webFileRequestOp = new UnityWebFileRequestOperation(webURL, savePath, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _webFileRequestOp);
}
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webFileRequestOp.Error;
WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(DownloadPackageManifestOperation));
}
}
}
private string GetDownloadRequestURL(string fileName)
{
// 轮流返回请求地址
if (_requestCount % 2 == 0)
return _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
return _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
}
}
}

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,257 @@
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class DownloadResumeFileOperation : DefaultDownloadFileOperation
{
private readonly DefaultCacheFileSystem _fileSystem;
private DownloadHandlerFileRange _downloadHandle;
private VerifyTempFileOperation _verifyOperation;
private bool _isReuqestLocalFile;
private long _fileOriginLength = 0;
private string _tempFilePath;
private ESteps _steps = ESteps.None;
internal DownloadResumeFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadParam param) : base(bundle, param)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_isReuqestLocalFile = DownloadSystemHelper.IsRequestLocalFile(Param.MainURL);
_tempFilePath = _fileSystem.GetTempFilePath(Bundle);
_steps = ESteps.CheckExists;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.CreateRequest;
}
}
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
FileUtility.CreateFileDirectory(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 获取下载起始位置
_fileOriginLength = 0;
long fileBeginLength = -1;
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= Bundle.FileSize)
{
// 删除临时文件
File.Delete(_tempFilePath);
}
else
{
fileBeginLength = fileInfo.Length;
_fileOriginLength = fileBeginLength;
DownloadedBytes = _fileOriginLength;
}
}
// 创建下载器
CreateWebRequest(fileBeginLength);
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _fileOriginLength + (long)_webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
_steps = ESteps.VerifyTempFile;
else
_steps = ESteps.TryAgain;
// 在遇到特殊错误的时候删除文件
ClearTempFileWhenError();
// 注意:最终释放请求器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
var element = new TempFileElement(_tempFilePath, Bundle.FileCRC, Bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
OperationSystem.StartOperation(_fileSystem.PackageName, _verifyOperation);
_steps = ESteps.CheckVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.CheckVerifyTempFile)
{
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
if (_fileSystem.WriteCacheBundleFile(Bundle, _tempFilePath))
{
Status = EOperationStatus.Succeed;
_steps = ESteps.Done;
}
else
{
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
}
}
else
{
Error = _verifyOperation.Error;
_steps = ESteps.TryAgain;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
//TODO 拷贝本地文件失败后不再尝试!
if (_isReuqestLocalFile)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalOnAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
}
internal override void InternalWaitForAsyncComplete()
{
//TODO 防止下载器挂起陷入无限死循环!
if (_steps == ESteps.None)
{
InternalOnStart();
}
while (true)
{
//TODO 如果是导入或解压本地文件,执行等待完毕
if (_isReuqestLocalFile)
{
InternalOnUpdate();
if (IsDone)
break;
}
else
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
private void CreateWebRequest(long beginLength)
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
#if UNITY_2019_4_OR_NEWER
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
#else
var handler = new DownloadHandlerFileRange(FileSavePath, Bundle.FileSize, _webRequest);
_downloadHandle = handler;
#endif
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
if (beginLength > 0)
_webRequest.SetRequestHeader("Range", $"bytes={beginLength}-");
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_downloadHandle != null)
{
_downloadHandle.Cleanup();
_downloadHandle = null;
}
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
private void ClearTempFileWhenError()
{
if (_fileSystem.ResumeDownloadResponseCodes == null)
return;
//说明:如果遇到以下错误返回码,验证失败直接删除文件
if (_fileSystem.ResumeDownloadResponseCodes.Contains(HttpCode))
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
}
}

View File

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

View File

@@ -0,0 +1,64 @@
using System.IO;
namespace YooAsset
{
internal class LoadCachePackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadPackageHash,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
internal LoadCachePackageHashOperation(DefaultCacheFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
internal override void InternalOnStart()
{
_steps = ESteps.LoadPackageHash;
}
internal override void InternalOnUpdate()
{
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;
Status = EOperationStatus.Failed;
Error = $"Can not found cache package hash file : {filePath}";
return;
}
PackageHash = FileUtility.ReadAllText(filePath);
if (string.IsNullOrEmpty(PackageHash))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Cache package hash file content is empty !";
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

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,102 @@
using System.IO;
namespace YooAsset
{
internal class LoadCachePackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private DeserializeManifestOperation _deserializer;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { private set; get; }
internal LoadCachePackageManifestOperation(DefaultCacheFileSystem fileSystem, string packageVersion, string packageHash)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_packageHash = packageHash;
}
internal override void InternalOnStart()
{
_steps = ESteps.LoadFileData;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadFileData)
{
string manifestFilePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
if (File.Exists(manifestFilePath))
{
_steps = ESteps.VerifyFileData;
_fileData = File.ReadAllBytes(manifestFilePath);
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Can not found cache manifest file : {manifestFilePath}";
}
}
if (_steps == ESteps.VerifyFileData)
{
string fileHash = HashUtility.BytesCRC32(_fileData);
if (fileHash == _packageHash)
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Failed to verify cache package manifest file!";
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializer == null)
{
_deserializer = new DeserializeManifestOperation(_fileData);
OperationSystem.StartOperation(_fileSystem.PackageName, _deserializer);
}
Progress = _deserializer.Progress;
if (_deserializer.IsDone == false)
return;
if (_deserializer.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Manifest = _deserializer.Manifest;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _deserializer.Error;
}
}
}
}
}

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,98 @@

namespace YooAsset
{
internal class RequestRemotePackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
internal string PackageVersion { set; get; }
internal RequestRemotePackageVersionOperation(DefaultCacheFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
internal override void InternalOnStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestRemotePackageVersionOperation));
_steps = ESteps.RequestPackageVersion;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_webTextRequestOp == null)
{
string fileName = YooAssetSettingsData.GetPackageVersionFileName(_fileSystem.PackageName);
string url = GetWebRequestURL(fileName);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
OperationSystem.StartOperation(_fileSystem.PackageName, _webTextRequestOp);
}
Progress = _webTextRequestOp.Progress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageVersion))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Remote package version file content is empty !";
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webTextRequestOp.Error;
WebRequestCounter.RecordRequestFailed(_fileSystem.PackageName, nameof(RequestRemotePackageVersionOperation));
}
}
}
private string GetWebRequestURL(string fileName)
{
string url;
// 轮流返回请求地址
if (_requestCount % 2 == 0)
url = _fileSystem.RemoteServices.GetRemoteMainURL(fileName);
else
url = _fileSystem.RemoteServices.GetRemoteFallbackURL(fileName);
// 在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,124 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal sealed class SearchCacheFilesOperation : AsyncOperationBase
{
private enum ESteps
{
None,
Prepare,
SearchFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private IEnumerator<DirectoryInfo> _filesEnumerator = null;
private float _verifyStartTime;
private ESteps _steps = ESteps.None;
/// <summary>
/// 需要验证的元素
/// </summary>
public readonly List<VerifyFileElement> Result = new List<VerifyFileElement>(5000);
internal SearchCacheFilesOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalOnStart()
{
_steps = ESteps.Prepare;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
DirectoryInfo rootDirectory = new DirectoryInfo(_fileSystem.GetCacheBundleFilesRoot());
if (rootDirectory.Exists)
{
var directorieInfos = rootDirectory.EnumerateDirectories();
_filesEnumerator = directorieInfos.GetEnumerator();
}
_steps = ESteps.SearchFiles;
}
if (_steps == ESteps.SearchFiles)
{
if (SearchFiles())
return;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Search cache files elapsed time {costTime:f1} seconds");
}
}
private bool SearchFiles()
{
if (_filesEnumerator == null)
return false;
bool isFindItem;
while (true)
{
isFindItem = _filesEnumerator.MoveNext();
if (isFindItem == false)
break;
var rootFoder = _filesEnumerator.Current;
var childDirectories = rootFoder.GetDirectories();
foreach (var chidDirectory in childDirectories)
{
string bundleGUID = chidDirectory.Name;
if (_fileSystem.IsRecordBundleFile(bundleGUID))
continue;
// 创建验证元素类
string fileRootPath = chidDirectory.FullName;
string dataFilePath = $"{fileRootPath}/{ DefaultCacheFileSystemDefine.BundleDataFileName}";
string infoFilePath = $"{fileRootPath}/{ DefaultCacheFileSystemDefine.BundleInfoFileName}";
// 存储的数据文件追加文件格式
if (_fileSystem.AppendFileExtension)
{
string dataFileExtension = FindDataFileExtension(chidDirectory);
if (string.IsNullOrEmpty(dataFileExtension) == false)
dataFilePath += dataFileExtension;
}
var element = new VerifyFileElement(_fileSystem.PackageName, bundleGUID, fileRootPath, dataFilePath, infoFilePath);
Result.Add(element);
}
if (OperationSystem.IsBusy)
break;
}
return isFindItem;
}
private string FindDataFileExtension(DirectoryInfo directoryInfo)
{
string dataFileExtension = string.Empty;
var fileInfos = directoryInfo.GetFiles();
foreach (var fileInfo in fileInfos)
{
if (fileInfo.Name.StartsWith(DefaultCacheFileSystemDefine.BundleDataFileName))
{
dataFileExtension = fileInfo.Extension;
break;
}
}
return dataFileExtension;
}
}
}

View File

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

View File

@@ -0,0 +1,173 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace YooAsset
{
/// <summary>
/// 缓存文件验证(线程版)
/// </summary>
internal sealed class VerifyCacheFilesOperation : AsyncOperationBase
{
private enum ESteps
{
None,
InitVerify,
UpdateVerify,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly EFileVerifyLevel _fileVerifyLevel;
private List<VerifyFileElement> _waitingList;
private List<VerifyFileElement> _verifyingList;
private int _verifyMaxNum;
private int _verifyTotalCount;
private float _verifyStartTime;
private int _succeedCount;
private int _failedCount;
private ESteps _steps = ESteps.None;
internal VerifyCacheFilesOperation(DefaultCacheFileSystem fileSystem, List<VerifyFileElement> elements)
{
_fileSystem = fileSystem;
_waitingList = elements;
_fileVerifyLevel = fileSystem.FileVerifyLevel;
}
internal override void InternalOnStart()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.InitVerify)
{
int fileCount = _waitingList.Count;
// 设置同时验证的最大数
ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads);
YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}");
_verifyMaxNum = Math.Min(workerThreads, ioThreads);
_verifyTotalCount = fileCount;
if (_verifyMaxNum < 1)
_verifyMaxNum = 1;
_verifyingList = new List<VerifyFileElement>(_verifyMaxNum);
_steps = ESteps.UpdateVerify;
}
if (_steps == ESteps.UpdateVerify)
{
// 检测校验结果
for (int i = _verifyingList.Count - 1; i >= 0; i--)
{
var verifyElement = _verifyingList[i];
int result = verifyElement.Result;
if (result != 0)
{
_verifyingList.RemoveAt(i);
RecordVerifyFile(verifyElement);
}
}
Progress = GetProgress();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Verify cache files elapsed time {costTime:f1} seconds");
}
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (OperationSystem.IsBusy)
break;
if (_verifyingList.Count >= _verifyMaxNum)
break;
var element = _waitingList[i];
bool succeed = ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), element);
if (succeed)
{
_waitingList.RemoveAt(i);
_verifyingList.Add(element);
}
else
{
YooLogger.Warning("The thread pool is failed queued.");
break;
}
}
}
}
private float GetProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(_succeedCount + _failedCount) / _verifyTotalCount;
}
private void VerifyInThread(object obj)
{
VerifyFileElement element = (VerifyFileElement)obj;
int verifyResult = (int)VerifyingCacheFile(element, _fileVerifyLevel);
element.Result = verifyResult;
}
private void RecordVerifyFile(VerifyFileElement element)
{
if (element.Result == (int)EFileVerifyResult.Succeed)
{
_succeedCount++;
var recordFileElement = new RecordFileElement(element.InfoFilePath, element.DataFilePath, element.DataFileCRC, element.DataFileSize);
_fileSystem.RecordBundleFile(element.BundleGUID, recordFileElement);
}
else
{
_failedCount++;
YooLogger.Warning($"Failed to verify file {element.Result} and delete files : {element.FileRootPath}");
element.DeleteFiles();
}
}
/// <summary>
/// 验证缓存文件(子线程内操作)
/// </summary>
private EFileVerifyResult VerifyingCacheFile(VerifyFileElement element, EFileVerifyLevel verifyLevel)
{
try
{
if (verifyLevel == EFileVerifyLevel.Low)
{
if (File.Exists(element.InfoFilePath) == false)
return EFileVerifyResult.InfoFileNotExisted;
if (File.Exists(element.DataFilePath) == false)
return EFileVerifyResult.DataFileNotExisted;
return EFileVerifyResult.Succeed;
}
else
{
if (File.Exists(element.InfoFilePath) == false)
return EFileVerifyResult.InfoFileNotExisted;
// 解析信息文件获取验证数据
_fileSystem.ReadBundleInfoFile(element.InfoFilePath, out element.DataFileCRC, out element.DataFileSize);
}
}
catch (Exception)
{
return EFileVerifyResult.Exception;
}
return FileVerifyHelper.FileVerify(element.DataFilePath, element.DataFileSize, element.DataFileCRC, verifyLevel);
}
}
}

View File

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

View File

@@ -0,0 +1,88 @@
using System;
using System.Threading;
namespace YooAsset
{
/// <summary>
/// 下载文件验证(线程版)
/// </summary>
internal sealed class VerifyTempFileOperation : AsyncOperationBase
{
private enum ESteps
{
None,
VerifyFile,
Waiting,
Done,
}
private readonly TempFileElement _element;
private ESteps _steps = ESteps.None;
/// <summary>
/// 验证结果
/// </summary>
public EFileVerifyResult VerifyResult { private set; get; }
internal VerifyTempFileOperation(TempFileElement element)
{
_element = element;
}
internal override void InternalOnStart()
{
_steps = ESteps.VerifyFile;
}
internal override void InternalOnUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.VerifyFile)
{
bool succeed = ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), _element);
if (succeed)
{
_steps = ESteps.Waiting;
}
}
if (_steps == ESteps.Waiting)
{
int result = _element.Result;
if (result == 0)
return;
VerifyResult = (EFileVerifyResult)result;
if (VerifyResult == EFileVerifyResult.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to verify file : {_element.TempFilePath} ! ErrorCode : {VerifyResult}";
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
//TODO 等待子线程验证文件完毕,该操作会挂起主线程
InternalOnUpdate();
if (IsDone)
break;
}
}
private void VerifyInThread(object obj)
{
TempFileElement element = (TempFileElement)obj;
int result = (int)FileVerifyHelper.FileVerify(element.TempFilePath, element.TempFileSize, element.TempFileCRC, EFileVerifyLevel.High);
element.Result = result;
}
}
}

View File

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