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

@@ -1,69 +0,0 @@
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal class AssetBundleResult : BundleResult
{
private readonly IFileSystem _fileSystem;
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
private readonly Stream _managedStream;
public AssetBundleResult(IFileSystem fileSystem, PackageBundle packageBundle, AssetBundle assetBundle, Stream managedStream)
{
_fileSystem = fileSystem;
_packageBundle = packageBundle;
_assetBundle = assetBundle;
_managedStream = managedStream;
}
public override void UnloadBundleFile()
{
if (_assetBundle != null)
{
_assetBundle.Unload(true);
}
if (_managedStream != null)
{
_managedStream.Close();
_managedStream.Dispose();
}
}
public override string GetBundleFilePath()
{
return _fileSystem.GetBundleFilePath(_packageBundle);
}
public override byte[] ReadBundleFileData()
{
return _fileSystem.ReadBundleFileData(_packageBundle);
}
public override string ReadBundleFileText()
{
return _fileSystem.ReadBundleFileText(_packageBundle);
}
public override FSLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
{
var operation = new AssetBundleLoadAssetOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
public override FSLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
{
var operation = new AssetBundleLoadAllAssetsOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
public override FSLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
{
var operation = new AssetBundleLoadSubAssetsOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
public override FSLoadSceneOperation LoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
{
var operation = new AssetBundleLoadSceneOperation(assetInfo, loadParams, suspendLoad);
return operation;
}
}
}

View File

@@ -1,121 +0,0 @@
using UnityEngine;
namespace YooAsset
{
internal class AssetBundleLoadAllAssetsOperation : FSLoadAllAssetsOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
private readonly AssetInfo _assetInfo;
private AssetBundleRequest _request;
private ESteps _steps = ESteps.None;
public AssetBundleLoadAllAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetBundle = assetBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
_steps = ESteps.CheckBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
if (_assetBundle == null)
{
_steps = ESteps.Done;
Error = $"The bundle {_packageBundle.BundleName} has been destroyed due to unity engine bugs !";
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (IsWaitForAsyncComplete)
{
if (_assetInfo.AssetType == null)
Result = _assetBundle.LoadAllAssets();
else
Result = _assetBundle.LoadAllAssets(_assetInfo.AssetType);
}
else
{
if (_assetInfo.AssetType == null)
_request = _assetBundle.LoadAllAssetsAsync();
else
_request = _assetBundle.LoadAllAssetsAsync(_assetInfo.AssetType);
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_request != null)
{
if (IsWaitForAsyncComplete)
{
// 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Suspend the main thread to load unity asset.");
Result = _request.allAssets;
}
else
{
Progress = _request.progress;
if (_request.isDone == false)
return;
Result = _request.allAssets;
}
}
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load all assets : {_assetInfo.AssetPath} AssetType : null AssetBundle : {_packageBundle.BundleName}";
else
error = $"Failed to load all assets : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType} AssetBundle : {_packageBundle.BundleName}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

@@ -1,121 +0,0 @@
using UnityEngine;
namespace YooAsset
{
internal class AssetBundleLoadAssetOperation : FSLoadAssetOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
private readonly AssetInfo _assetInfo;
private AssetBundleRequest _request;
private ESteps _steps = ESteps.None;
public AssetBundleLoadAssetOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetBundle = assetBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
_steps = ESteps.CheckBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
if (_assetBundle == null)
{
_steps = ESteps.Done;
Error = $"The bundle {_packageBundle.BundleName} has been destroyed due to unity engine bugs !";
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (IsWaitForAsyncComplete)
{
if (_assetInfo.AssetType == null)
Result = _assetBundle.LoadAsset(_assetInfo.AssetPath);
else
Result = _assetBundle.LoadAsset(_assetInfo.AssetPath, _assetInfo.AssetType);
}
else
{
if (_assetInfo.AssetType == null)
_request = _assetBundle.LoadAssetAsync(_assetInfo.AssetPath);
else
_request = _assetBundle.LoadAssetAsync(_assetInfo.AssetPath, _assetInfo.AssetType);
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_request != null)
{
if (IsWaitForAsyncComplete)
{
// 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Suspend the main thread to load unity asset.");
Result = _request.asset;
}
else
{
Progress = _request.progress;
if (_request.isDone == false)
return;
Result = _request.asset;
}
}
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load asset : {_assetInfo.AssetPath} AssetType : null AssetBundle : {_packageBundle.BundleName}";
else
error = $"Failed to load asset : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType} AssetBundle : {_packageBundle.BundleName}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

@@ -1,117 +0,0 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal class AssetBundleLoadSceneOperation : FSLoadSceneOperation
{
protected enum ESteps
{
None,
LoadScene,
CheckResult,
Done,
}
private readonly AssetInfo _assetInfo;
private readonly LoadSceneParameters _loadParams;
private bool _suspendLoad;
private AsyncOperation _asyncOperation;
private ESteps _steps = ESteps.None;
public AssetBundleLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
{
_assetInfo = assetInfo;
_loadParams = loadParams;
_suspendLoad = suspendLoad;
}
internal override void InternalStart()
{
_steps = ESteps.LoadScene;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadScene)
{
if (IsWaitForAsyncComplete)
{
// 注意:场景同步加载方法不会立即加载场景,而是在下一帧加载。
Result = SceneManager.LoadScene(_assetInfo.AssetPath, _loadParams);
_steps = ESteps.CheckResult;
}
else
{
// 注意如果场景不存在异步加载方法返回NULL
// 注意:即使是异步加载也要在当帧获取到场景对象
_asyncOperation = SceneManager.LoadSceneAsync(_assetInfo.AssetPath, _loadParams);
if (_asyncOperation != null)
{
_asyncOperation.allowSceneActivation = !_suspendLoad;
_asyncOperation.priority = 100;
Result = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
_steps = ESteps.CheckResult;
}
else
{
string error = $"Failed to load scene : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
}
}
if (_steps == ESteps.CheckResult)
{
if (_asyncOperation != null)
{
if (IsWaitForAsyncComplete)
{
//注意:场景加载无法强制异步转同步
YooLogger.Error("The scene is loading asyn !");
}
else
{
// 注意:在业务层中途可以取消挂起
if (_asyncOperation.allowSceneActivation == false)
{
if (_suspendLoad == false)
_asyncOperation.allowSceneActivation = true;
}
Progress = _asyncOperation.progress;
if (_asyncOperation.isDone == false)
return;
}
}
if (Result.IsValid())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
string error = $"The loaded scene is invalid : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
//注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法!
InternalUpdate();
}
public override void UnSuspendLoad()
{
_suspendLoad = false;
}
}
}

View File

@@ -1,121 +0,0 @@
using UnityEngine;
namespace YooAsset
{
internal class AssetBundleLoadSubAssetsOperation : FSLoadSubAssetsOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
private readonly AssetInfo _assetInfo;
private AssetBundleRequest _request;
private ESteps _steps = ESteps.None;
public AssetBundleLoadSubAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetBundle = assetBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
_steps = ESteps.CheckBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
if (_assetBundle == null)
{
_steps = ESteps.Done;
Error = $"The bundle {_packageBundle.BundleName} has been destroyed due to unity engine bugs !";
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (IsWaitForAsyncComplete)
{
if (_assetInfo.AssetType == null)
Result = _assetBundle.LoadAssetWithSubAssets(_assetInfo.AssetPath);
else
Result = _assetBundle.LoadAssetWithSubAssets(_assetInfo.AssetPath, _assetInfo.AssetType);
}
else
{
if (_assetInfo.AssetType == null)
_request = _assetBundle.LoadAssetWithSubAssetsAsync(_assetInfo.AssetPath);
else
_request = _assetBundle.LoadAssetWithSubAssetsAsync(_assetInfo.AssetPath, _assetInfo.AssetType);
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_request != null)
{
if (IsWaitForAsyncComplete)
{
// 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Suspend the main thread to load unity asset.");
Result = _request.allAssets;
}
else
{
Progress = _request.progress;
if (_request.isDone == false)
return;
Result = _request.allAssets;
}
}
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load sub assets : {_assetInfo.AssetPath} AssetType : null AssetBundle : {_packageBundle.BundleName}";
else
error = $"Failed to load sub assets : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType} AssetBundle : {_packageBundle.BundleName}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

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

View File

@@ -1,48 +0,0 @@
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal abstract class BundleResult
{
/// <summary>
/// 卸载资源包文件
/// </summary>
public abstract void UnloadBundleFile();
/// <summary>
/// 获取资源包文件的路径
/// </summary>
public abstract string GetBundleFilePath();
/// <summary>
/// 读取资源包文件的二进制数据
/// </summary>
public abstract byte[] ReadBundleFileData();
/// <summary>
/// 读取资源包文件的文本数据
/// </summary>
public abstract string ReadBundleFileText();
/// <summary>
/// 加载资源包内的资源对象
/// </summary>
public abstract FSLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo);
/// <summary>
/// 加载资源包内的所有资源对象
/// </summary>
public abstract FSLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo);
/// <summary>
/// 加载资源包内的资源对象及所有子资源对象
/// </summary>
public abstract FSLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo);
/// <summary>
/// 加载资源包内的场景对象
/// </summary>
public abstract FSLoadSceneOperation LoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad);
}
}

View File

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

View File

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

View File

@@ -1,15 +0,0 @@

namespace YooAsset
{
internal class RawBundleLoadAllAssetsOperation : FSLoadAllAssetsOperation
{
internal override void InternalStart()
{
Error = $"{nameof(RawBundleLoadAllAssetsOperation)} not support load all assets !";
Status = EOperationStatus.Failed;
}
internal override void InternalUpdate()
{
}
}
}

View File

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

View File

@@ -1,15 +0,0 @@

namespace YooAsset
{
internal class RawBundleLoadAssetOperation : FSLoadAssetOperation
{
internal override void InternalStart()
{
Error = $"{nameof(RawBundleLoadAssetOperation)} not support load asset !";
Status = EOperationStatus.Failed;
}
internal override void InternalUpdate()
{
}
}
}

View File

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

View File

@@ -1,18 +0,0 @@

namespace YooAsset
{
internal class RawBundleLoadSceneOperation : FSLoadSceneOperation
{
internal override void InternalStart()
{
Error = $"{nameof(RawBundleLoadSceneOperation)} not support load scene !";
Status = EOperationStatus.Failed;
}
internal override void InternalUpdate()
{
}
public override void UnSuspendLoad()
{
}
}
}

View File

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

View File

@@ -1,15 +0,0 @@

namespace YooAsset
{
internal class RawBundleLoadSubAssetsOperation : FSLoadSubAssetsOperation
{
internal override void InternalStart()
{
Error = $"{nameof(RawBundleLoadSubAssetsOperation)} not support load sub assets !";
Status = EOperationStatus.Failed;
}
internal override void InternalUpdate()
{
}
}
}

View File

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

View File

@@ -1,53 +0,0 @@
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal class RawBundleResult : BundleResult
{
private readonly IFileSystem _fileSystem;
private readonly PackageBundle _packageBundle;
public RawBundleResult(IFileSystem fileSystem, PackageBundle packageBundle)
{
_fileSystem = fileSystem;
_packageBundle = packageBundle;
}
public override void UnloadBundleFile()
{
}
public override string GetBundleFilePath()
{
return _fileSystem.GetBundleFilePath(_packageBundle);
}
public override byte[] ReadBundleFileData()
{
return _fileSystem.ReadBundleFileData(_packageBundle);
}
public override string ReadBundleFileText()
{
return _fileSystem.ReadBundleFileText(_packageBundle);
}
public override FSLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
{
var operation = new RawBundleLoadAssetOperation();
return operation;
}
public override FSLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
{
var operation = new RawBundleLoadAllAssetsOperation();
return operation;
}
public override FSLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
{
var operation = new RawBundleLoadSubAssetsOperation();
return operation;
}
public override FSLoadSceneOperation LoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
{
var operation = new RawBundleLoadSceneOperation();
return operation;
}
}
}

View File

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

View File

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

View File

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

View File

@@ -1,122 +0,0 @@
using System.Collections.Generic;
namespace YooAsset
{
internal class VirtualBundleLoadAllAssetsOperation : FSLoadAllAssetsOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None;
public VirtualBundleLoadAllAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
#if UNITY_EDITOR
_steps = ESteps.CheckBundle;
#else
_steps = ESteps.Done;
Error = $"{nameof(VirtualBundleLoadAllAssetsOperation)} only support unity editor platform !";
Status = EOperationStatus.Failed;
#endif
}
internal override void InternalUpdate()
{
#if UNITY_EDITOR
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
// 检测资源文件是否存在
string guid = UnityEditor.AssetDatabase.AssetPathToGUID(_assetInfo.AssetPath);
if (string.IsNullOrEmpty(guid))
{
string error = $"Not found asset : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (_assetInfo.AssetType == null)
{
List<UnityEngine.Object> result = new List<UnityEngine.Object>();
foreach (var packageAsset in _packageBundle.IncludeMainAssets)
{
string assetPath = packageAsset.AssetPath;
UnityEngine.Object mainAsset = UnityEditor.AssetDatabase.LoadMainAssetAtPath(assetPath);
if (mainAsset != null)
result.Add(mainAsset);
}
Result = result.ToArray();
}
else
{
List<UnityEngine.Object> result = new List<UnityEngine.Object>();
foreach (var packageAsset in _packageBundle.IncludeMainAssets)
{
string assetPath = packageAsset.AssetPath;
UnityEngine.Object mainAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath, _assetInfo.AssetType);
if (mainAsset != null)
result.Add(mainAsset);
}
Result = result.ToArray();
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load all assets : {_assetInfo.AssetPath} AssetType : null";
else
error = $"Failed to load all assets : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
#endif
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

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

View File

@@ -1,101 +0,0 @@

namespace YooAsset
{
internal class VirtualBundleLoadAssetOperation : FSLoadAssetOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None;
public VirtualBundleLoadAssetOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
#if UNITY_EDITOR
_steps = ESteps.CheckBundle;
#else
_steps = ESteps.Done;
Error = $"{nameof(VirtualBundleLoadAssetOperation)} only support unity editor platform !";
Status = EOperationStatus.Failed;
#endif
}
internal override void InternalUpdate()
{
#if UNITY_EDITOR
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
// 检测资源文件是否存在
string guid = UnityEditor.AssetDatabase.AssetPathToGUID(_assetInfo.AssetPath);
if (string.IsNullOrEmpty(guid))
{
string error = $"Not found asset : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (_assetInfo.AssetType == null)
Result = UnityEditor.AssetDatabase.LoadMainAssetAtPath(_assetInfo.AssetPath);
else
Result = UnityEditor.AssetDatabase.LoadAssetAtPath(_assetInfo.AssetPath, _assetInfo.AssetType);
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load asset object : {_assetInfo.AssetPath} AssetType : null";
else
error = $"Failed to load asset object : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
#endif
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

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

View File

@@ -1,123 +0,0 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal class VirtualBundleLoadSceneOperation : FSLoadSceneOperation
{
protected enum ESteps
{
None,
LoadScene,
CheckResult,
Done,
}
private readonly AssetInfo _assetInfo;
private readonly LoadSceneParameters _loadParams;
private bool _suspendLoad;
private AsyncOperation _asyncOperation;
private ESteps _steps = ESteps.None;
public VirtualBundleLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
{
_assetInfo = assetInfo;
_loadParams = loadParams;
_suspendLoad = suspendLoad;
}
internal override void InternalStart()
{
#if UNITY_EDITOR
_steps = ESteps.LoadScene;
#else
_steps = ESteps.Done;
Error = $"{nameof(VirtualBundleLoadSceneOperation)} only support unity editor platform !";
Status = EOperationStatus.Failed;
#endif
}
internal override void InternalUpdate()
{
#if UNITY_EDITOR
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadScene)
{
if (IsWaitForAsyncComplete)
{
Result = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneInPlayMode(_assetInfo.AssetPath, _loadParams);
_steps = ESteps.CheckResult;
}
else
{
_asyncOperation = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(_assetInfo.AssetPath, _loadParams);
if (_asyncOperation != null)
{
_asyncOperation.allowSceneActivation = !_suspendLoad;
_asyncOperation.priority = 100;
Result = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
_steps = ESteps.CheckResult;
}
else
{
string error = $"Failed to load scene : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
return;
}
}
}
if (_steps == ESteps.CheckResult)
{
if (_asyncOperation != null)
{
if (IsWaitForAsyncComplete)
{
// 注意:场景加载无法强制异步转同步
YooLogger.Error("The scene is loading asyn !");
}
else
{
// 注意:在业务层中途可以取消挂起
if (_asyncOperation.allowSceneActivation == false)
{
if (_suspendLoad == false)
_asyncOperation.allowSceneActivation = true;
}
Progress = _asyncOperation.progress;
if (_asyncOperation.isDone == false)
return;
}
}
if (Result.IsValid())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
string error = $"The loaded scene is invalid : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
}
#endif
}
internal override void InternalWaitForAsyncComplete()
{
//注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法!
InternalUpdate();
}
public override void UnSuspendLoad()
{
_suspendLoad = false;
}
}
}

View File

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

View File

@@ -1,113 +0,0 @@
using System.Collections.Generic;
namespace YooAsset
{
internal class VirtualBundleLoadSubAssetsOperation : FSLoadSubAssetsOperation
{
protected enum ESteps
{
None,
CheckBundle,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None;
public VirtualBundleLoadSubAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetInfo = assetInfo;
}
internal override void InternalStart()
{
#if UNITY_EDITOR
_steps = ESteps.CheckBundle;
#else
_steps = ESteps.Done;
Error = $"{nameof(VirtualBundleLoadSubAssetsOperation)} only support unity editor platform !";
Status = EOperationStatus.Failed;
#endif
}
internal override void InternalUpdate()
{
#if UNITY_EDITOR
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckBundle)
{
// 检测资源文件是否存在
string guid = UnityEditor.AssetDatabase.AssetPathToGUID(_assetInfo.AssetPath);
if (string.IsNullOrEmpty(guid))
{
string error = $"Not found asset : {_assetInfo.AssetPath}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (_assetInfo.AssetType == null)
{
Result = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(_assetInfo.AssetPath);
}
else
{
UnityEngine.Object[] findAssets = UnityEditor.AssetDatabase.LoadAllAssetRepresentationsAtPath(_assetInfo.AssetPath);
List<UnityEngine.Object> result = new List<UnityEngine.Object>(findAssets.Length);
foreach (var findAsset in findAssets)
{
if (_assetInfo.AssetType.IsAssignableFrom(findAsset.GetType()))
result.Add(findAsset);
}
Result = result.ToArray();
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load sub assets : {_assetInfo.AssetPath} AssetType : null";
else
error = $"Failed to load sub assets : {_assetInfo.AssetPath} AssetType : {_assetInfo.AssetType}";
YooLogger.Error(error);
_steps = ESteps.Done;
Error = error;
Status = EOperationStatus.Failed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
#endif
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

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

View File

@@ -1,53 +0,0 @@
using UnityEngine.SceneManagement;
namespace YooAsset
{
internal class VirtualBundleResult : BundleResult
{
private readonly IFileSystem _fileSystem;
private readonly PackageBundle _packageBundle;
public VirtualBundleResult(IFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_packageBundle = bundle;
}
public override void UnloadBundleFile()
{
}
public override string GetBundleFilePath()
{
return _fileSystem.GetBundleFilePath(_packageBundle);
}
public override byte[] ReadBundleFileData()
{
return _fileSystem.ReadBundleFileData(_packageBundle);
}
public override string ReadBundleFileText()
{
return _fileSystem.ReadBundleFileText(_packageBundle);
}
public override FSLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
{
var operation = new VirtualBundleLoadAssetOperation(_packageBundle, assetInfo);
return operation;
}
public override FSLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
{
var operation = new VirtualBundleLoadAllAssetsOperation(_packageBundle, assetInfo);
return operation;
}
public override FSLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
{
var operation = new VirtualBundleLoadSubAssetsOperation(_packageBundle, assetInfo);
return operation;
}
public override FSLoadSceneOperation LoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool suspendLoad)
{
var operation = new VirtualBundleLoadSceneOperation(assetInfo, loadParams, suspendLoad);
return operation;
}
}
}

View File

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

View File

@@ -1,21 +0,0 @@

namespace YooAsset
{
internal class CatalogDefine
{
/// <summary>
/// 文件极限大小100MB
/// </summary>
public const int FileMaxSize = 104857600;
/// <summary>
/// 文件头标记
/// </summary>
public const uint FileSign = 0x133C5EE;
/// <summary>
/// 文件格式版本
/// </summary>
public const string FileVersion = "1.0.0";
}
}

View File

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

View File

@@ -1,246 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
namespace YooAsset
{
internal static class CatalogTools
{
#if UNITY_EDITOR
/// <summary>
/// 生成包裹的内置资源目录文件
/// 说明:根据指定目录下的文件生成清单文件。
/// </summary>
public static bool CreateCatalogFile(IManifestRestoreServices services, string packageName, string packageDirectory)
{
// 获取资源清单版本
string packageVersion;
{
string versionFileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
string versionFilePath = $"{packageDirectory}/{versionFileName}";
if (File.Exists(versionFilePath) == false)
{
Debug.LogError($"Can not found package version file : {versionFilePath}");
return false;
}
packageVersion = FileUtility.ReadAllText(versionFilePath);
}
// 加载资源清单文件
PackageManifest packageManifest;
{
string manifestFileName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
string manifestFilePath = $"{packageDirectory}/{manifestFileName}";
if (File.Exists(manifestFilePath) == false)
{
Debug.LogError($"Can not found package manifest file : {manifestFilePath}");
return false;
}
var binaryData = FileUtility.ReadAllBytes(manifestFilePath);
packageManifest = ManifestTools.DeserializeFromBinary(binaryData, services);
}
// 获取文件名映射关系
Dictionary<string, string> fileMapping = new Dictionary<string, string>();
{
foreach (var packageBundle in packageManifest.BundleList)
{
fileMapping.Add(packageBundle.FileName, packageBundle.BundleGUID);
}
}
// 创建内置清单实例
var buildinFileCatalog = new DefaultBuildinFileCatalog();
buildinFileCatalog.FileVersion = CatalogDefine.FileVersion;
buildinFileCatalog.PackageName = packageName;
buildinFileCatalog.PackageVersion = packageVersion;
// 创建白名单查询集合
HashSet<string> whiteFileList = new HashSet<string>
{
"link.xml",
"buildlogtep.json",
DefaultBuildinFileSystemDefine.BuildinCatalogJsonFileName,
DefaultBuildinFileSystemDefine.BuildinCatalogBinaryFileName
};
string packageVersionFileName = YooAssetSettingsData.GetPackageVersionFileName(packageName);
string packageHashFileName = YooAssetSettingsData.GetPackageHashFileName(packageName, packageVersion);
string manifestBinaryFIleName = YooAssetSettingsData.GetManifestBinaryFileName(packageName, packageVersion);
string manifestJsonFIleName = YooAssetSettingsData.GetManifestJsonFileName(packageName, packageVersion);
string reportFileName = YooAssetSettingsData.GetBuildReportFileName(packageName, packageVersion);
whiteFileList.Add(packageVersionFileName);
whiteFileList.Add(packageHashFileName);
whiteFileList.Add(manifestBinaryFIleName);
whiteFileList.Add(manifestJsonFIleName);
whiteFileList.Add(reportFileName);
// 记录所有内置资源文件
DirectoryInfo rootDirectory = new DirectoryInfo(packageDirectory);
FileInfo[] fileInfos = rootDirectory.GetFiles();
foreach (var fileInfo in fileInfos)
{
if (fileInfo.Extension == ".meta")
continue;
if (whiteFileList.Contains(fileInfo.Name))
continue;
string fileName = fileInfo.Name;
if (fileMapping.TryGetValue(fileName, out string bundleGUID))
{
var wrapper = new DefaultBuildinFileCatalog.FileWrapper();
wrapper.BundleGUID = bundleGUID;
wrapper.FileName = fileName;
buildinFileCatalog.Wrappers.Add(wrapper);
}
else
{
Debug.LogWarning($"Failed mapping file : {fileName}");
}
}
// 创建输出文件
string jsonFilePath = $"{packageDirectory}/{DefaultBuildinFileSystemDefine.BuildinCatalogJsonFileName}";
if (File.Exists(jsonFilePath))
File.Delete(jsonFilePath);
SerializeToJson(jsonFilePath, buildinFileCatalog);
// 创建输出文件
string binaryFilePath = $"{packageDirectory}/{DefaultBuildinFileSystemDefine.BuildinCatalogBinaryFileName}";
if (File.Exists(binaryFilePath))
File.Delete(binaryFilePath);
SerializeToBinary(binaryFilePath, buildinFileCatalog);
UnityEditor.AssetDatabase.Refresh();
Debug.Log($"Succeed to save catalog file : {binaryFilePath}");
return true;
}
/// <summary>
/// 生成空的包裹内置资源目录文件
/// </summary>
public static bool CreateEmptyCatalogFile(string packageName, string packageVersion, string outputPath)
{
// 创建内置清单实例
var buildinFileCatalog = new DefaultBuildinFileCatalog();
buildinFileCatalog.FileVersion = CatalogDefine.FileVersion;
buildinFileCatalog.PackageName = packageName;
buildinFileCatalog.PackageVersion = packageVersion;
// 创建输出文件
string jsonFilePath = $"{outputPath}/{DefaultBuildinFileSystemDefine.BuildinCatalogJsonFileName}";
if (File.Exists(jsonFilePath))
File.Delete(jsonFilePath);
SerializeToJson(jsonFilePath, buildinFileCatalog);
// 创建输出文件
string binaryFilePath = $"{outputPath}/{DefaultBuildinFileSystemDefine.BuildinCatalogBinaryFileName}";
if (File.Exists(binaryFilePath))
File.Delete(binaryFilePath);
SerializeToBinary(binaryFilePath, buildinFileCatalog);
UnityEditor.AssetDatabase.Refresh();
Debug.Log($"Succeed to save catalog file : {binaryFilePath}");
return true;
}
#endif
/// <summary>
/// 序列化JSON文件
/// </summary>
public static void SerializeToJson(string savePath, DefaultBuildinFileCatalog catalog)
{
string json = JsonUtility.ToJson(catalog, true);
FileUtility.WriteAllText(savePath, json);
}
/// <summary>
/// 反序列化JSON文件
/// </summary>
public static DefaultBuildinFileCatalog DeserializeFromJson(string jsonContent)
{
return JsonUtility.FromJson<DefaultBuildinFileCatalog>(jsonContent);
}
/// <summary>
/// 序列化(二进制文件)
/// </summary>
public static void SerializeToBinary(string savePath, DefaultBuildinFileCatalog catalog)
{
using (FileStream fs = new FileStream(savePath, FileMode.Create))
{
// 创建缓存器
BufferWriter buffer = new BufferWriter(CatalogDefine.FileMaxSize);
// 写入文件标记
buffer.WriteUInt32(CatalogDefine.FileSign);
// 写入文件版本
buffer.WriteUTF8(CatalogDefine.FileVersion);
// 写入文件头信息
buffer.WriteUTF8(catalog.PackageName);
buffer.WriteUTF8(catalog.PackageVersion);
// 写入资源包列表
buffer.WriteInt32(catalog.Wrappers.Count);
for (int i = 0; i < catalog.Wrappers.Count; i++)
{
var fileWrapper = catalog.Wrappers[i];
buffer.WriteUTF8(fileWrapper.BundleGUID);
buffer.WriteUTF8(fileWrapper.FileName);
}
// 写入文件流
buffer.WriteToStream(fs);
fs.Flush();
}
}
/// <summary>
/// 反序列化(二进制文件)
/// </summary>
public static DefaultBuildinFileCatalog DeserializeFromBinary(byte[] binaryData)
{
if (binaryData == null || binaryData.Length == 0)
throw new Exception("Catalog file data is null or empty !");
// 创建缓存器
BufferReader buffer = new BufferReader(binaryData);
// 读取文件标记
uint fileSign = buffer.ReadUInt32();
if (fileSign != CatalogDefine.FileSign)
throw new Exception("Invalid catalog file !");
// 读取文件版本
string fileVersion = buffer.ReadUTF8();
if (fileVersion != CatalogDefine.FileVersion)
throw new Exception($"The catalog file version are not compatible : {fileVersion} != {CatalogDefine.FileVersion}");
DefaultBuildinFileCatalog catalog = new DefaultBuildinFileCatalog();
{
// 读取文件头信息
catalog.FileVersion = fileVersion;
catalog.PackageName = buffer.ReadUTF8();
catalog.PackageVersion = buffer.ReadUTF8();
// 读取资源包列表
int fileCount = buffer.ReadInt32();
catalog.Wrappers = new List<DefaultBuildinFileCatalog.FileWrapper>(fileCount);
for (int i = 0; i < fileCount; i++)
{
var fileWrapper = new DefaultBuildinFileCatalog.FileWrapper();
fileWrapper.BundleGUID = buffer.ReadUTF8();
fileWrapper.FileName = buffer.ReadUTF8();
catalog.Wrappers.Add(fileWrapper);
}
}
return catalog;
}
}
}

View File

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

View File

@@ -1,40 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// 内置资源清单目录
/// </summary>
[Serializable]
internal class DefaultBuildinFileCatalog
{
[Serializable]
public class FileWrapper
{
public string BundleGUID;
public string FileName;
}
/// <summary>
/// 文件版本
/// </summary>
public string FileVersion;
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion;
/// <summary>
/// 文件列表
/// </summary>
public List<FileWrapper> Wrappers = new List<FileWrapper>();
}
}

View File

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

View File

@@ -1,510 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 内置文件系统
/// </summary>
internal class DefaultBuildinFileSystem : IFileSystem
{
public class FileWrapper
{
public string FileName { private set; get; }
public FileWrapper(string fileName)
{
FileName = fileName;
}
}
protected readonly Dictionary<string, FileWrapper> _wrappers = new Dictionary<string, FileWrapper>(10000);
protected readonly Dictionary<string, string> _buildinFilePathMapping = new Dictionary<string, string>(10000);
protected IFileSystem _unpackFileSystem;
protected string _packageRoot;
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _packageRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return _wrappers.Count;
}
}
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { private set; get; }
/// <summary>
/// 自定义参数:覆盖安装缓存清理模式
/// </summary>
public EOverwriteInstallClearMode InstallClearMode { private set; get; } = EOverwriteInstallClearMode.ClearAllManifestFiles;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验级别
/// </summary>
public EFileVerifyLevel FileVerifyLevel { private set; get; } = EFileVerifyLevel.Middle;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验最大并发数
/// </summary>
public int FileVerifyMaxConcurrency { private set; get; } = 32;
/// <summary>
/// 自定义参数:数据文件追加文件格式
/// </summary>
public bool AppendFileExtension { private set; get; } = false;
/// <summary>
/// 自定义参数禁用Catalog目录查询文件
/// </summary>
public bool DisableCatalogFile { private set; get; } = false;
/// <summary>
/// 自定义参数:拷贝内置清单
/// </summary>
public bool CopyBuildinPackageManifest { private set; get; } = false;
/// <summary>
/// 自定义参数:拷贝内置清单的目标目录
/// 注意:该参数为空的时候,会获取默认的沙盒目录!
/// </summary>
public string CopyBuildinPackageManifestDestRoot { private set; get; }
/// <summary>
/// 自定义参数:解压文件系统的根目录
/// </summary>
public string UnpackFileSystemRoot { private set; get; }
/// <summary>
/// 自定义参数:解密服务接口的实例类
/// </summary>
public IDecryptionServices DecryptionServices { private set; get; }
/// <summary>
/// 自定义参数:资源清单服务类
/// </summary>
public IManifestRestoreServices ManifestServices { private set; get; }
/// <summary>
/// 自定义参数:拷贝内置文件接口的实例类
/// </summary>
public ICopyLocalFileServices CopyLocalFileServices { private set; get; }
#endregion
public DefaultBuildinFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new DBFSInitializeOperation(this);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new DBFSLoadPackageManifestOperation(this, packageVersion);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new DBFSRequestPackageVersionOperation(this);
return operation;
}
public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options)
{
return _unpackFileSystem.ClearCacheFilesAsync(manifest, options);
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{
// 注意:业务层的解压器会依赖该方法
options.ImportFilePath = GetBuildinFileLoadPath(bundle);
return _unpackFileSystem.DownloadFileAsync(bundle, options);
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
{
return _unpackFileSystem.LoadBundleFile(bundle);
}
if (bundle.BundleType == (int)EBuildBundleType.AssetBundle)
{
var operation = new DBFSLoadAssetBundleOperation(this, bundle);
return operation;
}
else if (bundle.BundleType == (int)EBuildBundleType.RawBundle)
{
var operation = new DBFSLoadRawBundleOperation(this, bundle);
return operation;
}
#if TUANJIE_1_7_OR_NEWER
else if (bundle.BundleType == (int)EBuildBundleType.InstantBundle)
{
var operation = new DBFSLoadInstantBundleOperation(this, bundle);
return operation;
}
#endif
else
{
string error = $"{nameof(DefaultBuildinFileSystem)} not support load bundle type : {bundle.BundleType}";
var operation = new FSLoadBundleCompleteOperation(error);
return operation;
}
}
public virtual void SetParameter(string name, object value)
{
if (name == FileSystemParametersDefine.DOWNLOAD_BACKEND)
{
DownloadBackend = (IDownloadBackend)value;
}
else if (name == FileSystemParametersDefine.UNITY_WEB_REQUEST_CREATOR)
{
WebRequestCreator = (UnityWebRequestCreator)value;
}
else if (name == FileSystemParametersDefine.INSTALL_CLEAR_MODE)
{
InstallClearMode = (EOverwriteInstallClearMode)value;
}
else if (name == FileSystemParametersDefine.FILE_VERIFY_LEVEL)
{
FileVerifyLevel = (EFileVerifyLevel)value;
}
else if (name == FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY)
{
int convertValue = Convert.ToInt32(value);
FileVerifyMaxConcurrency = Mathf.Clamp(convertValue, 1, int.MaxValue);
}
else if (name == FileSystemParametersDefine.APPEND_FILE_EXTENSION)
{
AppendFileExtension = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.DISABLE_CATALOG_FILE)
{
DisableCatalogFile = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST)
{
CopyBuildinPackageManifest = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST_DEST_ROOT)
{
CopyBuildinPackageManifestDestRoot = (string)value;
}
else if (name == FileSystemParametersDefine.UNPACK_FILE_SYSTEM_ROOT)
{
UnpackFileSystemRoot = (string)value;
}
else if (name == FileSystemParametersDefine.DECRYPTION_SERVICES)
{
DecryptionServices = (IDecryptionServices)value;
}
else if (name == FileSystemParametersDefine.MANIFEST_SERVICES)
{
ManifestServices = (IManifestRestoreServices)value;
}
else if (name == FileSystemParametersDefine.COPY_LOCAL_FILE_SERVICES)
{
CopyLocalFileServices = (ICopyLocalFileServices)value;
}
else
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
_packageRoot = GetDefaultBuildinPackageRoot(packageName);
else
_packageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
// 创建解压文件系统
var remoteServices = new DefaultUnpackRemoteServices(_packageRoot);
_unpackFileSystem = new DefaultUnpackFileSystem();
_unpackFileSystem.SetParameter(FileSystemParametersDefine.REMOTE_SERVICES, remoteServices);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.DOWNLOAD_BACKEND, DownloadBackend);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.UNITY_WEB_REQUEST_CREATOR, WebRequestCreator);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.INSTALL_CLEAR_MODE, InstallClearMode);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.FILE_VERIFY_LEVEL, FileVerifyLevel);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY, FileVerifyMaxConcurrency);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.APPEND_FILE_EXTENSION, AppendFileExtension);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.DECRYPTION_SERVICES, DecryptionServices);
_unpackFileSystem.SetParameter(FileSystemParametersDefine.COPY_LOCAL_FILE_SERVICES, CopyLocalFileServices);
_unpackFileSystem.OnCreate(packageName, UnpackFileSystemRoot);
}
public virtual void OnDestroy()
{
if (_unpackFileSystem != null)
{
_unpackFileSystem.OnDestroy();
_unpackFileSystem = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
public virtual bool Belong(PackageBundle bundle)
{
if (DisableCatalogFile)
return true;
return _wrappers.ContainsKey(bundle.BundleGUID);
}
public virtual bool Exists(PackageBundle bundle)
{
if (DisableCatalogFile)
return true;
return _wrappers.ContainsKey(bundle.BundleGUID);
}
public virtual bool NeedDownload(PackageBundle bundle)
{
return false;
}
public virtual bool NeedUnpack(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
{
return _unpackFileSystem.Exists(bundle) == false;
}
else
{
return false;
}
}
public virtual bool NeedImport(PackageBundle bundle)
{
return false;
}
public virtual string GetBundleFilePath(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
return _unpackFileSystem.GetBundleFilePath(bundle);
return GetBuildinFileLoadPath(bundle);
}
public virtual byte[] ReadBundleFileData(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
return _unpackFileSystem.ReadBundleFileData(bundle);
if (Exists(bundle) == false)
return null;
#if UNITY_ANDROID
//TODO : 安卓平台内置文件属于APK压缩包内的文件。
YooLogger.Error($"Android platform not support read buildin bundle file data !");
return null;
#else
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetBuildinFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileData(fileInfo);
}
else
{
string filePath = GetBuildinFileLoadPath(bundle);
return FileUtility.ReadAllBytes(filePath);
}
#endif
}
public virtual string ReadBundleFileText(PackageBundle bundle)
{
if (IsUnpackBundleFile(bundle))
return _unpackFileSystem.ReadBundleFileText(bundle);
if (Exists(bundle) == false)
return null;
#if UNITY_ANDROID
//TODO : 安卓平台内置文件属于APK压缩包内的文件。
YooLogger.Error($"Android platform not support read buildin bundle file text !");
return null;
#else
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetBuildinFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileText(fileInfo);
}
else
{
string filePath = GetBuildinFileLoadPath(bundle);
return FileUtility.ReadAllText(filePath);
}
#endif
}
/// <summary>
/// 是否属于解压资源包文件
/// </summary>
protected virtual bool IsUnpackBundleFile(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
#if UNITY_ANDROID || UNITY_OPENHARMONY
if (bundle.Encrypted)
return true;
if (bundle.BundleType == (int)EBuildBundleType.RawBundle)
return true;
return false;
#else
return false;
#endif
}
#region
protected string GetDefaultBuildinPackageRoot(string packageName)
{
string rootDirectory = YooAssetSettingsData.GetYooDefaultBuildinRoot();
return PathUtility.Combine(rootDirectory, packageName);
}
public string GetBuildinFileLoadPath(PackageBundle bundle)
{
if (_buildinFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = PathUtility.Combine(_packageRoot, bundle.FileName);
_buildinFilePathMapping.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
public string GetBuildinPackageVersionFilePath()
{
string fileName = YooAssetSettingsData.GetPackageVersionFileName(PackageName);
return PathUtility.Combine(_packageRoot, fileName);
}
public string GetBuildinPackageHashFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
public string GetBuildinPackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
public string GetCatalogBinaryFileLoadPath()
{
return PathUtility.Combine(_packageRoot, DefaultBuildinFileSystemDefine.BuildinCatalogBinaryFileName);
}
/// <summary>
/// 记录文件信息
/// </summary>
public bool RecordCatalogFile(string bundleGUID, FileWrapper wrapper)
{
if (_wrappers.ContainsKey(bundleGUID))
{
YooLogger.Error($"{nameof(DefaultBuildinFileSystem)} has element : {bundleGUID}");
return false;
}
_wrappers.Add(bundleGUID, wrapper);
return true;
}
/// <summary>
/// 初始化解压文件系统
/// </summary>
public FSInitializeFileSystemOperation InitializeUpackFileSystem()
{
return _unpackFileSystem.InitializeFileSystemAsync();
}
/// <summary>
/// 加载加密的资源文件
/// </summary>
public DecryptResult LoadEncryptedAssetBundle(PackageBundle bundle)
{
string filePath = GetBuildinFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.LoadAssetBundle(fileInfo);
}
/// <summary>
/// 加载加密的资源文件
/// </summary>
public DecryptResult LoadEncryptedAssetBundleAsync(PackageBundle bundle)
{
string filePath = GetBuildinFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.LoadAssetBundleAsync(fileInfo);
}
#endregion
}
}

View File

@@ -1,16 +0,0 @@

namespace YooAsset
{
internal class DefaultBuildinFileSystemDefine
{
/// <summary>
/// 内置清单JSON文件名称
/// </summary>
public const string BuildinCatalogJsonFileName = "BuildinCatalog.json";
/// <summary>
/// 内置清单二进制文件名称
/// </summary>
public const string BuildinCatalogBinaryFileName = "BuildinCatalog.bytes";
}
}

View File

@@ -1,238 +0,0 @@
using System;
using System.IO;
namespace YooAsset
{
internal class DBFSInitializeOperation : FSInitializeFileSystemOperation
{
private enum ESteps
{
None,
LoadBuildinPackageVersion,
CopyBuildinPackageHash,
CopyBuildinPackageManifest,
InitUnpackFileSystem,
LoadCatalogFile,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private RequestBuildinPackageVersionOperation _requestBuildinPackageVersionOp;
private CopyBuildinFileOperation _copyBuildinHashFileOp;
private CopyBuildinFileOperation _copyBuildinManifestFileOp;
private FSInitializeFileSystemOperation _initUnpackFIleSystemOp;
private LoadBuildinCatalogFileOperation _loadBuildinCatalogFileOp;
private ESteps _steps = ESteps.None;
internal DBFSInitializeOperation(DefaultBuildinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
#if UNITY_WEBGL
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{nameof(DefaultBuildinFileSystem)} is not support WEBGL platform !";
#else
if (_fileSystem.CopyBuildinPackageManifest)
_steps = ESteps.LoadBuildinPackageVersion;
else
_steps = ESteps.InitUnpackFileSystem;
#endif
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBuildinPackageVersion)
{
if (_requestBuildinPackageVersionOp == null)
{
_requestBuildinPackageVersionOp = new RequestBuildinPackageVersionOperation(_fileSystem);
_requestBuildinPackageVersionOp.StartOperation();
AddChildOperation(_requestBuildinPackageVersionOp);
}
_requestBuildinPackageVersionOp.UpdateOperation();
if (_requestBuildinPackageVersionOp.IsDone == false)
return;
if (_requestBuildinPackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CopyBuildinPackageHash;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _requestBuildinPackageVersionOp.Error;
}
}
if (_steps == ESteps.CopyBuildinPackageHash)
{
if (_copyBuildinHashFileOp == null)
{
string packageVersion = _requestBuildinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageHashDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuildinPackageHashFilePath(packageVersion);
_copyBuildinHashFileOp = new CopyBuildinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuildinHashFileOp.StartOperation();
AddChildOperation(_copyBuildinHashFileOp);
}
_copyBuildinHashFileOp.UpdateOperation();
if (_copyBuildinHashFileOp.IsDone == false)
return;
if (_copyBuildinHashFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CopyBuildinPackageManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _copyBuildinHashFileOp.Error;
}
}
if (_steps == ESteps.CopyBuildinPackageManifest)
{
if (_copyBuildinManifestFileOp == null)
{
string packageVersion = _requestBuildinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageManifestDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuildinPackageManifestFilePath(packageVersion);
_copyBuildinManifestFileOp = new CopyBuildinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuildinManifestFileOp.StartOperation();
AddChildOperation(_copyBuildinManifestFileOp);
}
_copyBuildinManifestFileOp.UpdateOperation();
if (_copyBuildinManifestFileOp.IsDone == false)
return;
if (_copyBuildinManifestFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.InitUnpackFileSystem;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _copyBuildinManifestFileOp.Error;
}
}
if (_steps == ESteps.InitUnpackFileSystem)
{
if (_initUnpackFIleSystemOp == null)
{
_initUnpackFIleSystemOp = _fileSystem.InitializeUpackFileSystem();
_initUnpackFIleSystemOp.StartOperation();
AddChildOperation(_initUnpackFIleSystemOp);
}
_initUnpackFIleSystemOp.UpdateOperation();
Progress = _initUnpackFIleSystemOp.Progress;
if (_initUnpackFIleSystemOp.IsDone == false)
return;
if (_initUnpackFIleSystemOp.Status == EOperationStatus.Succeed)
{
if (_fileSystem.DisableCatalogFile)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.LoadCatalogFile;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _initUnpackFIleSystemOp.Error;
}
}
if (_steps == ESteps.LoadCatalogFile)
{
if (_loadBuildinCatalogFileOp == null)
{
_loadBuildinCatalogFileOp = new LoadBuildinCatalogFileOperation(_fileSystem);
_loadBuildinCatalogFileOp.StartOperation();
AddChildOperation(_loadBuildinCatalogFileOp);
}
_loadBuildinCatalogFileOp.UpdateOperation();
if (_loadBuildinCatalogFileOp.IsDone == false)
return;
if (_loadBuildinCatalogFileOp.Status == EOperationStatus.Succeed)
{
var catalog = _loadBuildinCatalogFileOp.Catalog;
if (catalog == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Fatal error : catalog is null !";
return;
}
if (catalog.PackageName != _fileSystem.PackageName)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Catalog file package name {catalog.PackageName} cannot match the file system package name {_fileSystem.PackageName}";
return;
}
foreach (var wrapper in catalog.Wrappers)
{
var fileWrapper = new DefaultBuildinFileSystem.FileWrapper(wrapper.FileName);
_fileSystem.RecordCatalogFile(wrapper.BundleGUID, fileWrapper);
}
YooLogger.Log($"Package '{_fileSystem.PackageName}' buildin catalog files count : {catalog.Wrappers.Count}");
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadBuildinCatalogFileOp.Error;
}
}
}
private string GetCopyManifestFileRoot()
{
string destRoot = _fileSystem.CopyBuildinPackageManifestDestRoot;
if (string.IsNullOrEmpty(destRoot))
{
string defaultCacheRoot = YooAssetSettingsData.GetYooDefaultCacheRoot();
destRoot = PathUtility.Combine(defaultCacheRoot, _fileSystem.PackageName, DefaultCacheFileSystemDefine.ManifestFilesFolderName);
}
return destRoot;
}
private string GetCopyPackageHashDestPath(string packageVersion)
{
string fileRoot = GetCopyManifestFileRoot();
string fileName = YooAssetSettingsData.GetPackageHashFileName(_fileSystem.PackageName, packageVersion);
return PathUtility.Combine(fileRoot, fileName);
}
private string GetCopyPackageManifestDestPath(string packageVersion)
{
string fileRoot = GetCopyManifestFileRoot();
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_fileSystem.PackageName, packageVersion);
return PathUtility.Combine(fileRoot, fileName);
}
}
}

View File

@@ -1,217 +0,0 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 加载AssetBundle文件
/// </summary>
internal class DBFSLoadAssetBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
LoadAssetBundle,
CheckResult,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private AssetBundleCreateRequest _createRequest;
private AssetBundle _assetBundle;
private Stream _managedStream;
private ESteps _steps = ESteps.None;
internal DBFSLoadAssetBundleOperation(DefaultBuildinFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalStart()
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.LoadAssetBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
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.GetBuildinFileLoadPath(_bundle);
_assetBundle = AssetBundle.LoadFromFile(filePath);
}
}
else
{
if (_bundle.Encrypted)
{
var decryptResult = _fileSystem.LoadEncryptedAssetBundleAsync(_bundle);
_createRequest = decryptResult.CreateRequest;
_managedStream = decryptResult.ManagedStream;
}
else
{
string filePath = _fileSystem.GetBuildinFileLoadPath(_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)
{
if (_bundle.Encrypted)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to load encrypted buildin asset bundle file : {_bundle.BundleName}";
YooLogger.Error(Error);
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to load buildin asset bundle file : {_bundle.BundleName}";
YooLogger.Error(Error);
}
}
else
{
_steps = ESteps.Done;
Result = new AssetBundleResult(_fileSystem, _bundle, _assetBundle, _managedStream);
Status = EOperationStatus.Succeed;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
/// <summary>
/// 加载原生文件
/// </summary>
internal class DBFSLoadRawBundleOperation : FSLoadBundleOperation
{
private enum ESteps
{
None,
LoadBuildinRawBundle,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private ESteps _steps = ESteps.None;
internal DBFSLoadRawBundleOperation(DefaultBuildinFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalStart()
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.LoadBuildinRawBundle;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBuildinRawBundle)
{
string filePath = _fileSystem.GetBuildinFileLoadPath(_bundle);
#if UNITY_ANDROID
//TODO : 安卓平台内置文件属于APK压缩包内的文件。
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Can not load android buildin raw bundle file : {filePath}";
YooLogger.Error(Error);
#else
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 buildin raw bundle file : {filePath}";
YooLogger.Error(Error);
}
#endif
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

@@ -1,89 +0,0 @@

namespace YooAsset
{
internal class DBFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestBuildinPackageHash,
LoadBuildinPackageManifest,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _packageVersion;
private RequestBuildinPackageHashOperation _requestBuildinPackageHashOp;
private LoadBuildinPackageManifestOperation _loadBuildinPackageManifestOp;
private ESteps _steps = ESteps.None;
public DBFSLoadPackageManifestOperation(DefaultBuildinFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
internal override void InternalStart()
{
_steps = ESteps.RequestBuildinPackageHash;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestBuildinPackageHash)
{
if (_requestBuildinPackageHashOp == null)
{
_requestBuildinPackageHashOp = new RequestBuildinPackageHashOperation(_fileSystem, _packageVersion);
_requestBuildinPackageHashOp.StartOperation();
AddChildOperation(_requestBuildinPackageHashOp);
}
_requestBuildinPackageHashOp.UpdateOperation();
if (_requestBuildinPackageHashOp.IsDone == false)
return;
if (_requestBuildinPackageHashOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadBuildinPackageManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _requestBuildinPackageHashOp.Error;
}
}
if (_steps == ESteps.LoadBuildinPackageManifest)
{
if (_loadBuildinPackageManifestOp == null)
{
string packageHash = _requestBuildinPackageHashOp.PackageHash;
_loadBuildinPackageManifestOp = new LoadBuildinPackageManifestOperation(_fileSystem, _packageVersion, packageHash);
_loadBuildinPackageManifestOp.StartOperation();
AddChildOperation(_loadBuildinPackageManifestOp);
}
_loadBuildinPackageManifestOp.UpdateOperation();
if (_loadBuildinPackageManifestOp.IsDone == false)
return;
if (_loadBuildinPackageManifestOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Manifest = _loadBuildinPackageManifestOp.Manifest;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadBuildinPackageManifestOp.Error;
}
}
}
}
}

View File

@@ -1,59 +0,0 @@

namespace YooAsset
{
internal class DBFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private RequestBuildinPackageVersionOperation _requestBuildinPackageVersionOp;
private ESteps _steps = ESteps.None;
internal DBFSRequestPackageVersionOperation(DefaultBuildinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.RequestPackageVersion;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_requestBuildinPackageVersionOp == null)
{
_requestBuildinPackageVersionOp = new RequestBuildinPackageVersionOperation(_fileSystem);
_requestBuildinPackageVersionOp.StartOperation();
AddChildOperation(_requestBuildinPackageVersionOp);
}
_requestBuildinPackageVersionOp.UpdateOperation();
if (_requestBuildinPackageVersionOp.IsDone == false)
return;
if (_requestBuildinPackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
PackageVersion = _requestBuildinPackageVersionOp.PackageVersion;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _requestBuildinPackageVersionOp.Error;
}
}
}
}
}

View File

@@ -1,103 +0,0 @@
using System;
using System.IO;
namespace YooAsset
{
internal class CopyBuildinFileOperation : AsyncOperationBase
{
private enum ESteps
{
None,
CheckFileExist,
TryCopyFile,
UnpackFile,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _sourceFilePath;
private readonly string _destFilePath;
private IDownloadFileRequest _webFileRequestOp;
private ESteps _steps = ESteps.None;
public CopyBuildinFileOperation(DefaultBuildinFileSystem fileSystem, string sourceFilePath, string destFilePath)
{
_fileSystem = fileSystem;
_sourceFilePath = sourceFilePath;
_destFilePath = destFilePath;
}
internal override void InternalStart()
{
_steps = ESteps.CheckFileExist;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckFileExist)
{
if (File.Exists(_destFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.TryCopyFile;
}
}
if (_steps == ESteps.TryCopyFile)
{
if (File.Exists(_sourceFilePath))
{
try
{
var directory = Path.GetDirectoryName(_destFilePath);
if (Directory.Exists(directory) == false)
Directory.CreateDirectory(directory);
File.Copy(_sourceFilePath, _destFilePath, true);
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
catch (Exception ex)
{
YooLogger.Warning($"Failed copy buildin file : {ex.Message}");
_steps = ESteps.UnpackFile;
}
}
else
{
_steps = ESteps.UnpackFile;
}
}
if (_steps == ESteps.UnpackFile)
{
if (_webFileRequestOp == null)
{
string url = DownloadSystemHelper.ConvertToWWWPath(_sourceFilePath);
var args = new DownloadFileRequestArgs(url, _destFilePath, 60, 0);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webFileRequestOp.Error;
}
}
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
using System.IO;
namespace YooAsset
{
internal sealed class LoadBuildinCatalogFileOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadFileData,
RequestFileData,
LoadCatalog,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private IDownloadBytesRequest _webDataRequestOp;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 内置资源目录
/// </summary>
public DefaultBuildinFileCatalog Catalog;
internal LoadBuildinCatalogFileOperation(DefaultBuildinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.TryLoadFileData;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadFileData)
{
string filePath = _fileSystem.GetCatalogBinaryFileLoadPath();
if (File.Exists(filePath))
{
_fileData = File.ReadAllBytes(filePath);
_steps = ESteps.LoadCatalog;
}
else
{
_steps = ESteps.RequestFileData;
}
}
if (_steps == ESteps.RequestFileData)
{
if (_webDataRequestOp == null)
{
string filePath = _fileSystem.GetCatalogBinaryFileLoadPath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_fileData = _webDataRequestOp.Result;
_steps = ESteps.LoadCatalog;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webDataRequestOp.Error;
}
}
if (_steps == ESteps.LoadCatalog)
{
try
{
Catalog = CatalogTools.DeserializeFromBinary(_fileData);
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
catch (Exception ex)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed to load catalog file : {ex.Message}";
}
}
}
}
}

View File

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

View File

@@ -1,134 +0,0 @@
using System.IO;
namespace YooAsset
{
internal class LoadBuildinPackageManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadFileData,
RequestFileData,
VerifyFileData,
LoadManifest,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private IDownloadBytesRequest _webDataRequestOp;
private DeserializeManifestOperation _deserializer;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹清单
/// </summary>
public PackageManifest Manifest { private set; get; }
internal LoadBuildinPackageManifestOperation(DefaultBuildinFileSystem fileSystem, string packageVersion, string packageHash)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_packageHash = packageHash;
}
internal override void InternalStart()
{
_steps = ESteps.TryLoadFileData;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadFileData)
{
string filePath = _fileSystem.GetBuildinPackageManifestFilePath(_packageVersion);
if (File.Exists(filePath))
{
_fileData = File.ReadAllBytes(filePath);
_steps = ESteps.VerifyFileData;
}
else
{
_steps = ESteps.RequestFileData;
}
}
if (_steps == ESteps.RequestFileData)
{
if (_webDataRequestOp == null)
{
string filePath = _fileSystem.GetBuildinPackageManifestFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_fileData = _webDataRequestOp.Result;
_steps = ESteps.VerifyFileData;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webDataRequestOp.Error;
}
}
if (_steps == ESteps.VerifyFileData)
{
if (ManifestTools.VerifyManifestData(_fileData, _packageHash))
{
_steps = ESteps.LoadManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Failed to verify buildin package manifest file !";
}
}
if (_steps == ESteps.LoadManifest)
{
if (_deserializer == null)
{
_deserializer = new DeserializeManifestOperation(_fileSystem.ManifestServices, _fileData);
_deserializer.StartOperation();
AddChildOperation(_deserializer);
}
_deserializer.UpdateOperation();
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;
}
}
}
internal override string InternalGetDesc()
{
return $"PackageVersion : {_packageVersion} PackageHash : {_packageHash}";
}
}
}

View File

@@ -1,98 +0,0 @@
using System.IO;
namespace YooAsset
{
internal class RequestBuildinPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadPackageHash,
RequestPackageHash,
CheckResult,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _packageVersion;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
internal RequestBuildinPackageHashOperation(DefaultBuildinFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
internal override void InternalStart()
{
_steps = ESteps.TryLoadPackageHash;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadPackageHash)
{
string filePath = _fileSystem.GetBuildinPackageHashFilePath(_packageVersion);
if (File.Exists(filePath))
{
PackageHash = File.ReadAllText(filePath);
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.RequestPackageHash;
}
}
if (_steps == ESteps.RequestPackageHash)
{
if (_webTextRequestOp == null)
{
string filePath = _fileSystem.GetBuildinPackageHashFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageHash = _webTextRequestOp.Result;
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webTextRequestOp.Error;
}
}
if (_steps == ESteps.CheckResult)
{
if (string.IsNullOrEmpty(PackageHash))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Buildin package hash file content is empty !";
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

View File

@@ -1,96 +0,0 @@
using System.IO;
namespace YooAsset
{
internal class RequestBuildinPackageVersionOperation : AsyncOperationBase
{
private enum ESteps
{
None,
TryLoadPackageVersion,
RequestPackageVersion,
CheckResult,
Done,
}
private readonly DefaultBuildinFileSystem _fileSystem;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹版本
/// </summary>
public string PackageVersion { private set; get; }
internal RequestBuildinPackageVersionOperation(DefaultBuildinFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_steps = ESteps.TryLoadPackageVersion;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.TryLoadPackageVersion)
{
string filePath = _fileSystem.GetBuildinPackageVersionFilePath();
if (File.Exists(filePath))
{
PackageVersion = File.ReadAllText(filePath);
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.RequestPackageVersion;
}
}
if (_steps == ESteps.RequestPackageVersion)
{
if (_webTextRequestOp == null)
{
string filePath = _fileSystem.GetBuildinPackageVersionFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _webTextRequestOp.Error;
}
}
if (_steps == ESteps.CheckResult)
{
if (string.IsNullOrEmpty(PackageVersion))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Buildin package version file content is empty !";
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

View File

@@ -1,488 +0,0 @@
# DefaultBuildinFileSystem 内置文件系统
## 模块概述
DefaultBuildinFileSystem 是 YooAsset 的**内置资源文件系统**用于管理打包到应用程序中的资源文件StreamingAssets。该文件系统支持 AssetBundle 和原生文件的加载,并内置解压文件系统以处理 Android/OpenHarmony 平台的特殊需求。
### 核心特性
- **内置资源管理**:管理 StreamingAssets 目录下的资源文件
- **Catalog 目录系统**:使用目录文件快速查询内置资源
- **自动解压机制**Android/OpenHarmony 平台自动解压加密和原生文件
- **清单拷贝功能**:支持将内置清单拷贝到沙盒目录
- **加密资源支持**:通过解密服务接口支持加密资源加载
---
## 设计目标
| 目标 | 说明 |
|------|------|
| **跨平台支持** | 统一处理各平台 StreamingAssets 的访问差异 |
| **高效查询** | 通过 Catalog 文件快速判断资源是否内置 |
| **自动解压** | 自动处理 Android 平台无法直接访问的资源 |
| **灵活配置** | 支持多种参数配置适应不同需求 |
---
## 文件结构
```
DefaultBuildinFileSystem/
├── DefaultBuildinFileSystem.cs # 文件系统主类
├── DefaultBuildinFileSystemDefine.cs # 常量定义
├── DefaultBuildinFileCatalog.cs # 内置资源目录结构
├── CatalogDefine.cs # Catalog 文件格式定义
├── CatalogTools.cs # Catalog 序列化工具
└── Operation/ # 操作类
├── DBFSInitializeOperation.cs # 初始化操作
├── DBFSRequestPackageVersionOperation.cs # 请求版本操作
├── DBFSLoadPackageManifestOperation.cs # 加载清单操作
├── DBFSLoadBundleOperation.cs # 加载资源包操作
└── internal/ # 内部操作类
├── CopyBuildinFileOperation.cs # 拷贝内置文件操作
├── LoadBuildinCatalogFileOperation.cs # 加载 Catalog 文件操作
├── LoadBuildinPackageManifestOperation.cs# 加载清单文件操作
├── RequestBuildinPackageHashOperation.cs # 请求哈希文件操作
└── RequestBuildinPackageVersionOperation.cs # 请求版本文件操作
```
---
## 核心类说明
### DefaultBuildinFileSystem
内置文件系统的主类,实现 `IFileSystem` 接口。
#### 基本属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `PackageName` | `string` | 包裹名称 |
| `FileRoot` | `string` | 文件根目录StreamingAssets 下的包裹目录) |
| `FileCount` | `int` | 已记录的内置文件数量 |
| `DownloadBackend` | `IDownloadBackend` | 下载后台接口 |
#### 自定义参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `InstallClearMode` | `EOverwriteInstallClearMode` | `ClearAllManifestFiles` | 覆盖安装时的缓存清理模式 |
| `FileVerifyLevel` | `EFileVerifyLevel` | `Middle` | 文件校验级别 |
| `FileVerifyMaxConcurrency` | `int` | `32` | 文件校验最大并发数 |
| `AppendFileExtension` | `bool` | `false` | 是否追加文件扩展名 |
| `DisableCatalogFile` | `bool` | `false` | 禁用 Catalog 目录文件 |
| `CopyBuildinPackageManifest` | `bool` | `false` | 是否拷贝内置清单到沙盒 |
| `CopyBuildinPackageManifestDestRoot` | `string` | `null` | 清单拷贝目标目录 |
| `UnpackFileSystemRoot` | `string` | `null` | 解压文件系统根目录 |
| `DecryptionServices` | `IDecryptionServices` | `null` | 解密服务接口 |
| `ManifestServices` | `IManifestRestoreServices` | `null` | 清单恢复服务接口 |
| `CopyLocalFileServices` | `ICopyLocalFileServices` | `null` | 本地文件拷贝服务接口 |
#### 核心方法
```csharp
// 生命周期
void OnCreate(string packageName, string packageRoot);
void OnDestroy();
void SetParameter(string name, object value);
// 异步操作
FSInitializeFileSystemOperation InitializeFileSystemAsync();
FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout);
FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout);
FSLoadBundleOperation LoadBundleFile(PackageBundle bundle);
FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options);
FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options);
// 文件查询
bool Belong(PackageBundle bundle); // 检查是否属于内置文件
bool Exists(PackageBundle bundle); // 检查文件是否存在
bool NeedDownload(PackageBundle bundle);// 始终返回 false
bool NeedUnpack(PackageBundle bundle); // 检查是否需要解压
bool NeedImport(PackageBundle bundle); // 始终返回 false
// 文件访问
string GetBundleFilePath(PackageBundle bundle);
byte[] ReadBundleFileData(PackageBundle bundle);
string ReadBundleFileText(PackageBundle bundle);
```
---
## Catalog 目录系统
### DefaultBuildinFileCatalog
内置资源目录结构,记录所有内置资源文件的信息。
```csharp
[Serializable]
internal class DefaultBuildinFileCatalog
{
[Serializable]
public class FileWrapper
{
public string BundleGUID; // 资源包 GUID
public string FileName; // 文件名
}
public string FileVersion; // 文件版本
public string PackageName; // 包裹名称
public string PackageVersion; // 包裹版本
public List<FileWrapper> Wrappers; // 文件列表
}
```
### CatalogDefine
Catalog 文件格式常量定义。
```csharp
internal class CatalogDefine
{
public const int FileMaxSize = 104857600; // 文件极限大小100MB
public const uint FileSign = 0x133C5EE; // 文件头标记
public const string FileVersion = "1.0.0"; // 文件格式版本
}
```
### CatalogTools
Catalog 文件的序列化和反序列化工具。
| 方法 | 说明 |
|------|------|
| `CreateCatalogFile()` | 生成包裹的内置资源目录文件(编辑器) |
| `CreateEmptyCatalogFile()` | 生成空的内置资源目录文件(编辑器) |
| `SerializeToJson()` | 序列化为 JSON 文件 |
| `DeserializeFromJson()` | 从 JSON 文件反序列化 |
| `SerializeToBinary()` | 序列化为二进制文件 |
| `DeserializeFromBinary()` | 从二进制文件反序列化 |
---
## 操作类说明
### DBFSInitializeOperation
初始化操作,执行以下步骤:
```
状态流程:
┌─────────────────────────────────────────────────────────────┐
│ CopyBuildinPackageManifest = true ? │
│ │ │
│ ├── Yes ──► LoadBuildinPackageVersion │
│ │ └── RequestBuildinPackageVersionOp │
│ │ ↓ │
│ │ CopyBuildinPackageHash │
│ │ └── CopyBuildinFileOperation │
│ │ ↓ │
│ │ CopyBuildinPackageManifest │
│ │ └── CopyBuildinFileOperation │
│ │ ↓ │
│ └── No ─────────────────┘ │
│ ↓ │
│ InitUnpackFileSystem │
│ └── DefaultUnpackFileSystem.Init │
│ ↓ │
│ DisableCatalogFile = true ? │
│ ├── Yes ──► Done (Succeed) │
│ └── No ──► LoadCatalogFile │
│ └── LoadBuildinCatalog │
│ ↓ │
│ RecordCatalogFile │
│ ↓ │
│ Done (Succeed) │
└─────────────────────────────────────────────────────────────┘
```
### DBFSLoadBundleOperation
加载资源包操作,支持多种资源类型。
#### DBFSLoadAssetBundleOperation
加载 AssetBundle 文件。
```
状态流程:
LoadAssetBundle
├── 加密资源 ──► DecryptionServices.LoadAssetBundle[Async]
└── 普通资源 ──► AssetBundle.LoadFromFile[Async]
CheckResult
├── 成功 ──► AssetBundleResult
└── 失败 ──► Error
```
#### DBFSLoadRawBundleOperation
加载原生文件。
```
状态流程:
LoadBuildinRawBundle
├── Android 平台 ──► Error不支持直接读取
└── 其他平台 ──► RawBundleResult
```
#### DBFSLoadInstantBundleOperation
加载团结引擎Tuanjie专用资源包需要 `TUANJIE_1_7_OR_NEWER` 宏)。
---
## 内部操作类
### LoadBuildinCatalogFileOperation
加载 Catalog 目录文件。
```
状态流程:
TryLoadFileData
├── 文件存在 ──► File.ReadAllBytes
└── 文件不存在 ──► RequestFileData (UnityWebRequest)
LoadCatalog
└── CatalogTools.DeserializeFromBinary
```
### CopyBuildinFileOperation
拷贝内置文件到目标路径。
```
状态流程:
CheckFileExist
├── 目标已存在 ──► Done (Succeed)
└── 目标不存在 ──► TryCopyFile
TryCopyFile
├── 源文件存在 ──► File.Copy
└── 源文件不存在 ──► UnpackFile (UnityWebRequest)
```
---
## 解压机制
### 自动解压条件
在 Android/OpenHarmony 平台上,以下情况需要解压到沙盒:
```csharp
protected virtual bool IsUnpackBundleFile(PackageBundle bundle)
{
#if UNITY_ANDROID || UNITY_OPENHARMONY
if (bundle.Encrypted) // 加密资源
return true;
if (bundle.BundleType == RawBundle) // 原生文件
return true;
return false;
#else
return false;
#endif
}
```
### 解压文件系统
内置文件系统在创建时会自动创建一个 `DefaultUnpackFileSystem` 实例:
```csharp
public virtual void OnCreate(string packageName, string packageRoot)
{
// 创建解压文件系统
var remoteServices = new DefaultUnpackRemoteServices(_packageRoot);
_unpackFileSystem = new DefaultUnpackFileSystem();
_unpackFileSystem.SetParameter(REMOTE_SERVICES, remoteServices);
_unpackFileSystem.SetParameter(FILE_VERIFY_LEVEL, FileVerifyLevel);
// ... 其他参数
_unpackFileSystem.OnCreate(packageName, UnpackFileSystemRoot);
}
```
---
## 平台差异处理
### Android 平台限制
```
┌─────────────────────────────────────────────────────────────┐
│ Android 平台特殊处理 │
├─────────────────────────────────────────────────────────────┤
│ StreamingAssets 文件位于 APK 压缩包内,无法直接访问: │
│ │
│ ✓ AssetBundle.LoadFromFile 支持Unity 内部处理) │
│ ✗ File.ReadAllBytes 不支持 │
│ ✗ File.Exists 不支持 │
│ ✓ UnityWebRequest 支持jar:file:// 协议) │
│ │
│ 解决方案: │
│ 1. 加密资源 → 自动解压到沙盒 │
│ 2. 原生文件 → 自动解压到沙盒 │
│ 3. Catalog → 使用 UnityWebRequest 读取 │
└─────────────────────────────────────────────────────────────┘
```
### WebGL 平台
```csharp
#if UNITY_WEBGL
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{nameof(DefaultBuildinFileSystem)} is not support WEBGL platform !";
#endif
```
WebGL 平台不支持 DefaultBuildinFileSystem应使用 `DefaultWebServerFileSystem`
---
## 使用示例
### 基础配置
```csharp
// 创建内置文件系统参数
var buildinParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
// 初始化包裹
var initParams = new OfflinePlayModeParameters();
initParams.BuildinFileSystemParameters = buildinParams;
var initOp = package.InitializeAsync(initParams);
```
### 配置解密服务
```csharp
var buildinParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
// 设置解密服务
buildinParams.AddParameter(
FileSystemParametersDefine.DECRYPTION_SERVICES,
new MyDecryptionServices()
);
```
### 配置清单拷贝
```csharp
var buildinParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
// 启用清单拷贝(用于离线模式切换到联机模式)
buildinParams.AddParameter(
FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST,
true
);
// 可选:指定拷贝目标目录
buildinParams.AddParameter(
FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST_DEST_ROOT,
"/custom/path"
);
```
### 禁用 Catalog 文件
```csharp
var buildinParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
// 禁用 Catalog所有资源视为内置
buildinParams.AddParameter(
FileSystemParametersDefine.DISABLE_CATALOG_FILE,
true
);
```
### 配置解压文件系统根目录
```csharp
var buildinParams = FileSystemParameters.CreateDefaultBuildinFileSystemParameters();
// 设置解压文件系统的根目录
buildinParams.AddParameter(
FileSystemParametersDefine.UNPACK_FILE_SYSTEM_ROOT,
"/custom/unpack/path"
);
```
---
## 参数常量
```csharp
// 安装清理
FileSystemParametersDefine.INSTALL_CLEAR_MODE // EOverwriteInstallClearMode
// 文件校验
FileSystemParametersDefine.FILE_VERIFY_LEVEL // EFileVerifyLevel
FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY // int
// 文件配置
FileSystemParametersDefine.APPEND_FILE_EXTENSION // bool
FileSystemParametersDefine.DISABLE_CATALOG_FILE // bool
// 清单拷贝
FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST // bool
FileSystemParametersDefine.COPY_BUILDIN_PACKAGE_MANIFEST_DEST_ROOT // string
// 解压配置
FileSystemParametersDefine.UNPACK_FILE_SYSTEM_ROOT // string
// 服务接口
FileSystemParametersDefine.DECRYPTION_SERVICES // IDecryptionServices
FileSystemParametersDefine.MANIFEST_SERVICES // IManifestRestoreServices
FileSystemParametersDefine.COPY_LOCAL_FILE_SERVICES // ICopyLocalFileServices
```
---
## 类继承关系
```
IFileSystem
└── DefaultBuildinFileSystem
└── (内部持有) DefaultUnpackFileSystem
FSInitializeFileSystemOperation
└── DBFSInitializeOperation
FSRequestPackageVersionOperation
└── DBFSRequestPackageVersionOperation
FSLoadPackageManifestOperation
└── DBFSLoadPackageManifestOperation
FSLoadBundleOperation
├── DBFSLoadAssetBundleOperation
├── DBFSLoadRawBundleOperation
└── DBFSLoadInstantBundleOperation (Tuanjie)
AsyncOperationBase
├── LoadBuildinCatalogFileOperation
├── CopyBuildinFileOperation
├── LoadBuildinPackageManifestOperation
├── RequestBuildinPackageHashOperation
└── RequestBuildinPackageVersionOperation
BundleResult
├── AssetBundleResult ← AssetBundle 资源
└── RawBundleResult ← 原生文件
```
---
## 注意事项
1. **WebGL 不支持**DefaultBuildinFileSystem 不支持 WebGL 平台
2. **Android 限制**Android 平台无法直接读取 StreamingAssets 中的原生文件
3. **Catalog 文件**:构建时需要生成 Catalog 文件,否则需要禁用 Catalog 功能
4. **解压目录**:解压的文件存储在 `UnpackFileSystemRoot` 指定的目录
5. **加密资源**:加密资源在 Android/OpenHarmony 平台会自动解压到沙盒
6. **清单拷贝**:启用清单拷贝可以支持从离线模式平滑切换到联机模式

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 7ec531875d1515b4496e0e9035e63661
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,63 +0,0 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 应用程序水印
/// </summary>
internal class ApplicationFootPrint
{
private readonly DefaultCacheFileSystem _fileSystem;
private string _footPrint;
public ApplicationFootPrint(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
/// <summary>
/// 读取应用程序水印
/// </summary>
public void Load(string packageName)
{
string footPrintFilePath = _fileSystem.GetSandboxAppFootPrintFilePath();
if (File.Exists(footPrintFilePath))
{
_footPrint = FileUtility.ReadAllText(footPrintFilePath);
}
else
{
Coverage(packageName);
}
}
/// <summary>
/// 检测水印是否发生变化
/// </summary>
public bool IsDirty()
{
#if UNITY_EDITOR
return _footPrint != Application.version;
#else
return _footPrint != Application.buildGUID;
#endif
}
/// <summary>
/// 覆盖掉水印
/// </summary>
public void Coverage(string packageName)
{
#if UNITY_EDITOR
_footPrint = Application.version;
#else
_footPrint = Application.buildGUID;
#endif
string footPrintFilePath = _fileSystem.GetSandboxAppFootPrintFilePath();
FileUtility.WriteAllText(footPrintFilePath, _footPrint);
YooLogger.Log($"Save application foot print : {_footPrint}");
}
}
}

View File

@@ -1,695 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 缓存文件系统
/// 说明正在进行的下载器会在ResourcePackage销毁的时候执行Abort操作
/// </summary>
internal class DefaultCacheFileSystem : IFileSystem
{
protected readonly Dictionary<string, RecordFileElement> _records = new Dictionary<string, RecordFileElement>(10000);
protected readonly Dictionary<string, string> _bundleDataFilePathMapping = new Dictionary<string, string>(10000);
protected readonly Dictionary<string, string> _bundleInfoFilePathMapping = new Dictionary<string, string>(10000);
protected readonly Dictionary<string, string> _tempFilePathMapping = new Dictionary<string, string>(10000);
protected string _packageRoot;
protected string _tempFilesRoot;
protected string _cacheBundleFilesRoot;
protected string _cacheManifestFilesRoot;
/// <summary>
/// 下载调度器
/// </summary>
public DownloadSchedulerOperation DownloadScheduler { set; get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _packageRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return _records.Count;
}
}
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { private set; get; }
/// <summary>
/// 自定义参数:远程服务接口的实例类
/// </summary>
public IRemoteServices RemoteServices { private set; get; }
/// <summary>
/// 自定义参数:覆盖安装缓存清理模式
/// </summary>
public EOverwriteInstallClearMode InstallClearMode { private set; get; } = EOverwriteInstallClearMode.ClearAllManifestFiles;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验级别
/// </summary>
public EFileVerifyLevel FileVerifyLevel { private set; get; } = EFileVerifyLevel.Middle;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验最大并发数
/// 默认值32推荐范围 1-128
/// 说明:过大的值可能导致线程池任务过多,影响系统稳定性
/// </summary>
public int FileVerifyMaxConcurrency { private set; get; } = 32;
/// <summary>
/// 自定义参数:数据文件追加文件格式
/// </summary>
public bool AppendFileExtension { private set; get; } = false;
/// <summary>
/// 自定义参数:禁用边玩边下机制
/// </summary>
public bool DisableOnDemandDownload { private set; get; } = false;
/// <summary>
/// 自定义参数:最大并发连接数
/// 默认值10推荐范围 1-32
/// 说明:过大的并发数可能被服务器限流,也会增加本地资源消耗
/// </summary>
public int DownloadMaxConcurrency { private set; get; } = 10;
/// <summary>
/// 自定义参数:每帧发起的最大请求数
/// 默认值5推荐范围 1-10
/// 说明:避免单帧发起过多请求导致卡顿
/// </summary>
public int DownloadMaxRequestPerFrame { private set; get; } = 5;
/// <summary>
/// 自定义参数:下载任务的看门狗机制监控时间
/// </summary>
public int DownloadWatchDogTime { private set; get; } = 0;
/// <summary>
/// 自定义参数:启用断点续传的最小尺寸
/// </summary>
public long ResumeDownloadMinimumSize { private set; get; } = long.MaxValue;
/// <summary>
/// 自定义参数:断点续传下载器关注的错误码
/// </summary>
public List<long> ResumeDownloadResponseCodes { private set; get; } = null;
/// <summary>
/// 自定义参数:解密服务接口的实例类
/// </summary>
public IDecryptionServices DecryptionServices { private set; get; }
/// <summary>
/// 自定义参数:资源清单服务类
/// </summary>
public IManifestRestoreServices ManifestServices { private set; get; }
/// <summary>
/// 自定义参数:拷贝内置文件接口的实例类
/// </summary>
public ICopyLocalFileServices CopyLocalFileServices { private set; get; }
#endregion
public DefaultCacheFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new DCFSInitializeOperation(this);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new DCFSLoadPackageManifestOperation(this, packageVersion, timeout);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new DCFSRequestPackageVersionOperation(this, appendTimeTicks, timeout);
return operation;
}
public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options)
{
if (options.ClearMode == EFileClearMode.ClearAllBundleFiles.ToString())
{
var operation = new ClearAllCacheBundleFilesOperation(this);
return operation;
}
else if (options.ClearMode == EFileClearMode.ClearUnusedBundleFiles.ToString())
{
var operation = new ClearUnusedCacheBundleFilesOperation(this, manifest);
return operation;
}
else if (options.ClearMode == EFileClearMode.ClearBundleFilesByLocations.ToString())
{
var operation = new ClearCacheBundleFilesByLocationsOperaiton(this, manifest, options.ClearParam);
return operation;
}
else if (options.ClearMode == EFileClearMode.ClearBundleFilesByTags.ToString())
{
var operation = new ClearCacheBundleFilesByTagsOperaiton(this, manifest, options.ClearParam);
return operation;
}
else if (options.ClearMode == EFileClearMode.ClearAllManifestFiles.ToString())
{
var operation = new ClearAllCacheManifestFilesOperation(this);
return operation;
}
else if (options.ClearMode == EFileClearMode.ClearUnusedManifestFiles.ToString())
{
var operation = new ClearUnusedCacheManifestFilesOperation(this, manifest);
return operation;
}
else
{
string error = $"Invalid clear mode : {options.ClearMode}";
var operation = new FSClearCacheFilesCompleteOperation(error);
return operation;
}
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{
// 获取下载地址
if (string.IsNullOrEmpty(options.ImportFilePath))
{
// 注意:如果是解压文件系统类,这里会返回本地内置文件的下载路径
string mainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
string fallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
options.SetURL(mainURL, fallbackURL);
}
else
{
// 注意:把本地导入文件路径转换为下载器请求地址
string mainURL = DownloadSystemHelper.ConvertToWWWPath(options.ImportFilePath);
options.SetURL(mainURL, mainURL);
}
var downloader = new DownloadPackageBundleOperation(this, bundle, options);
return downloader;
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (bundle.BundleType == (int)EBuildBundleType.AssetBundle)
{
var operation = new DCFSLoadAssetBundleOperation(this, bundle);
return operation;
}
else if (bundle.BundleType == (int)EBuildBundleType.RawBundle)
{
var operation = new DCFSLoadRawBundleOperation(this, bundle);
return operation;
}
else
{
string error = $"{nameof(DefaultCacheFileSystem)} not support load bundle type : {bundle.BundleType}";
var operation = new FSLoadBundleCompleteOperation(error);
return operation;
}
}
public virtual void SetParameter(string name, object value)
{
if (name == FileSystemParametersDefine.DOWNLOAD_BACKEND)
{
DownloadBackend = (IDownloadBackend)value;
}
else if (name == FileSystemParametersDefine.UNITY_WEB_REQUEST_CREATOR)
{
WebRequestCreator = (UnityWebRequestCreator)value;
}
else if (name == FileSystemParametersDefine.REMOTE_SERVICES)
{
RemoteServices = (IRemoteServices)value;
}
else if (name == FileSystemParametersDefine.INSTALL_CLEAR_MODE)
{
InstallClearMode = (EOverwriteInstallClearMode)value;
}
else if (name == FileSystemParametersDefine.FILE_VERIFY_LEVEL)
{
FileVerifyLevel = (EFileVerifyLevel)value;
}
else if (name == FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY)
{
int convertValue = Convert.ToInt32(value);
if (convertValue > 256)
{
YooLogger.Warning($"FILE_VERIFY_MAX_CONCURRENCY value {convertValue} is too large, clamped to 256. Recommended range: 1 - 128.");
}
// 限制在合理范围内1-256
// 超过 256 的并发数对于文件验证来说没有意义,反而会增加线程池压力
FileVerifyMaxConcurrency = Mathf.Clamp(convertValue, 1, 256);
}
else if (name == FileSystemParametersDefine.APPEND_FILE_EXTENSION)
{
AppendFileExtension = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.DISABLE_ONDEMAND_DOWNLOAD)
{
DisableOnDemandDownload = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.DOWNLOAD_MAX_CONCURRENCY)
{
int convertValue = Convert.ToInt32(value);
if (convertValue > 64)
{
YooLogger.Warning($"DOWNLOAD_MAX_CONCURRENCY value {convertValue} is too large, clamped to 64. Recommended range: 1 - 32.");
}
DownloadMaxConcurrency = Mathf.Clamp(convertValue, 1, 64);
}
else if (name == FileSystemParametersDefine.DOWNLOAD_MAX_REQUEST_PER_FRAME)
{
int convertValue = Convert.ToInt32(value);
if (convertValue > 20)
{
YooLogger.Warning($"DOWNLOAD_MAX_REQUEST_PER_FRAME value {convertValue} is too large, clamped to 20. Recommended range: 1 - 10.");
}
DownloadMaxRequestPerFrame = Mathf.Clamp(convertValue, 1, 20);
}
else if (name == FileSystemParametersDefine.DOWNLOAD_WATCH_DOG_TIME)
{
int convertValue = Convert.ToInt32(value);
DownloadWatchDogTime = Mathf.Clamp(convertValue, 0, int.MaxValue);
}
else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE)
{
ResumeDownloadMinimumSize = Convert.ToInt64(value);
}
else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_RESPONSE_CODES)
{
ResumeDownloadResponseCodes = (List<long>)value;
}
else if (name == FileSystemParametersDefine.DECRYPTION_SERVICES)
{
DecryptionServices = (IDecryptionServices)value;
}
else if (name == FileSystemParametersDefine.MANIFEST_SERVICES)
{
ManifestServices = (IManifestRestoreServices)value;
}
else if (name == FileSystemParametersDefine.COPY_LOCAL_FILE_SERVICES)
{
CopyLocalFileServices = (ICopyLocalFileServices)value;
}
else
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
_packageRoot = GetDefaultCachePackageRoot(packageName);
else
_packageRoot = packageRoot;
_cacheBundleFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.BundleFilesFolderName);
_cacheManifestFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.ManifestFilesFolderName);
_tempFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.TempFilesFolderName);
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
}
public virtual void OnDestroy()
{
if (DownloadScheduler != null)
{
DownloadScheduler.Dispose();
DownloadScheduler = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
public virtual bool Belong(PackageBundle bundle)
{
// 注意:缓存文件系统保底加载!
return true;
}
public virtual bool Exists(PackageBundle bundle)
{
return _records.ContainsKey(bundle.BundleGUID);
}
public virtual bool NeedDownload(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
return Exists(bundle) == false;
}
public virtual bool NeedUnpack(PackageBundle bundle)
{
return false;
}
public virtual bool NeedImport(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
return Exists(bundle) == false;
}
public virtual string GetBundleFilePath(PackageBundle bundle)
{
return GetCacheBundleFileLoadPath(bundle);
}
public virtual byte[] ReadBundleFileData(PackageBundle bundle)
{
if (Exists(bundle) == false)
return null;
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetCacheBundleFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileData(fileInfo);
}
else
{
string filePath = GetCacheBundleFileLoadPath(bundle);
return FileUtility.ReadAllBytes(filePath);
}
}
public virtual string ReadBundleFileText(PackageBundle bundle)
{
if (Exists(bundle) == false)
return null;
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetCacheBundleFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileText(fileInfo);
}
else
{
string filePath = GetCacheBundleFileLoadPath(bundle);
return FileUtility.ReadAllText(filePath);
}
}
#region
public List<string> GetAllCachedBundleGUIDs()
{
return _records.Keys.ToList();
}
public RecordFileElement GetRecordFileElement(PackageBundle bundle)
{
if (_records.TryGetValue(bundle.BundleGUID, out RecordFileElement element))
return element;
else
return null;
}
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;
}
public string GetBundleDataFilePath(PackageBundle bundle)
{
if (_bundleDataFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
string folderName = bundle.FileHash.Substring(0, 2);
filePath = PathUtility.Combine(_cacheBundleFilesRoot, folderName, bundle.BundleGUID, DefaultCacheFileSystemDefine.BundleDataFileName);
if (AppendFileExtension)
filePath += bundle.FileExtension;
_bundleDataFilePathMapping.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
public string GetBundleInfoFilePath(PackageBundle bundle)
{
if (_bundleInfoFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
string folderName = bundle.FileHash.Substring(0, 2);
filePath = PathUtility.Combine(_cacheBundleFilesRoot, folderName, bundle.BundleGUID, DefaultCacheFileSystemDefine.BundleInfoFileName);
_bundleInfoFilePathMapping.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
public bool IsRecordBundleFile(string bundleGUID)
{
return _records.ContainsKey(bundleGUID);
}
public bool RecordBundleFile(string bundleGUID, RecordFileElement element)
{
if (_records.ContainsKey(bundleGUID))
{
YooLogger.Error($"{nameof(DefaultCacheFileSystem)} has element : {bundleGUID}");
return false;
}
_records.Add(bundleGUID, element);
return true;
}
public EFileVerifyResult VerifyCacheFile(PackageBundle bundle)
{
if (_records.TryGetValue(bundle.BundleGUID, out RecordFileElement element) == false)
return EFileVerifyResult.CacheNotFound;
EFileVerifyResult result = FileVerifyHelper.FileVerify(element.DataFilePath, element.DataFileSize, element.DataFileCRC, EFileVerifyLevel.High);
return result;
}
public bool WriteCacheBundleFile(PackageBundle bundle, string copyPath)
{
if (_records.ContainsKey(bundle.BundleGUID))
{
throw new YooInternalException();
}
string infoFilePath = GetBundleInfoFilePath(bundle);
string dataFilePath = GetBundleDataFilePath(bundle);
try
{
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
FileUtility.CreateFileDirectory(dataFilePath);
// 拷贝数据文件
FileInfo fileInfo = new FileInfo(copyPath);
fileInfo.CopyTo(dataFilePath);
// 写入文件信息
WriteBundleInfoFile(infoFilePath, bundle.FileCRC, bundle.FileSize);
}
catch (Exception ex)
{
YooLogger.Error($"Failed to write cache file ! {ex.Message}");
return false;
}
var recordFileElement = new RecordFileElement(infoFilePath, dataFilePath, bundle.FileCRC, bundle.FileSize);
return RecordBundleFile(bundle.BundleGUID, recordFileElement);
}
public bool DeleteCacheBundleFile(string bundleGUID)
{
if (_records.TryGetValue(bundleGUID, out RecordFileElement element))
{
_records.Remove(bundleGUID);
return element.DeleteFolder();
}
else
{
return false;
}
}
private readonly BufferWriter _sharedBuffer = new BufferWriter(1024);
public void WriteBundleInfoFile(string filePath, uint dataFileCRC, long dataFileSize)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
_sharedBuffer.Clear();
_sharedBuffer.WriteUInt32(dataFileCRC);
_sharedBuffer.WriteInt64(dataFileSize);
_sharedBuffer.WriteToStream(fs);
fs.Flush();
}
}
public void ReadBundleInfoFile(string filePath, out uint dataFileCRC, out long dataFileSize)
{
byte[] binaryData = FileUtility.ReadAllBytes(filePath);
BufferReader buffer = new BufferReader(binaryData);
dataFileCRC = buffer.ReadUInt32();
dataFileSize = buffer.ReadInt64();
}
#endregion
#region
public string GetDefaultCachePackageRoot(string packageName)
{
string rootDirectory = YooAssetSettingsData.GetYooDefaultCacheRoot();
return PathUtility.Combine(rootDirectory, packageName);
}
public string GetCacheBundleFileLoadPath(PackageBundle bundle)
{
return GetBundleDataFilePath(bundle);
}
public string GetCachePackageHashFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_cacheManifestFilesRoot, fileName);
}
public string GetCachePackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_cacheManifestFilesRoot, fileName);
}
public string GetSandboxAppFootPrintFilePath()
{
return PathUtility.Combine(_cacheManifestFilesRoot, DefaultCacheFileSystemDefine.AppFootPrintFileName);
}
public string GetCacheBundleFilesRoot()
{
return _cacheBundleFilesRoot;
}
public string GetCacheManifestFilesRoot()
{
return _cacheManifestFilesRoot;
}
/// <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 DecryptResult LoadEncryptedAssetBundle(PackageBundle bundle)
{
string filePath = GetCacheBundleFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.LoadAssetBundle(fileInfo);
}
/// <summary>
/// 加载加密资源文件
/// </summary>
public DecryptResult LoadEncryptedAssetBundleAsync(PackageBundle bundle)
{
string filePath = GetCacheBundleFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.LoadAssetBundleAsync(fileInfo);
}
/// <summary>
/// 加载加密资源文件
/// </summary>
public DecryptResult LoadEncryptedAssetBundleFallback(PackageBundle bundle)
{
string filePath = GetCacheBundleFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.LoadAssetBundleFallback(fileInfo);
}
#endregion
}
}

View File

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

View File

@@ -1,47 +0,0 @@
using System;
using System.IO;
namespace YooAsset
{
internal class RecordFileElement
{
public string InfoFilePath { private set; get; }
public string DataFilePath { private set; get; }
public uint DataFileCRC { private set; get; }
public long DataFileSize { private set; get; }
public RecordFileElement(string infoFilePath, string dataFilePath, uint dataFileCRC, long dataFileSize)
{
InfoFilePath = infoFilePath;
DataFilePath = dataFilePath;
DataFileCRC = dataFileCRC;
DataFileSize = dataFileSize;
}
/// <summary>
/// 删除记录文件
/// </summary>
public bool DeleteFolder()
{
try
{
string directory = Path.GetDirectoryName(InfoFilePath);
DirectoryInfo directoryInfo = new DirectoryInfo(directory);
if (directoryInfo.Exists)
{
directoryInfo.Delete(true);
return true;
}
else
{
return false;
}
}
catch (Exception ex)
{
YooLogger.Error($"Failed to delete cache file ! {ex.Message}");
return false;
}
}
}
}

View File

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

View File

@@ -1,22 +0,0 @@

namespace YooAsset
{
internal class TempFileElement
{
public string TempFilePath { private set; get; }
public uint TempFileCRC { private set; get; }
public long TempFileSize { private set; get; }
/// <summary>
/// 注意:原子操作对象
/// </summary>
public volatile int Result = 0;
public TempFileElement(string filePath, uint fileCRC, long fileSize)
{
TempFilePath = filePath;
TempFileCRC = fileCRC;
TempFileSize = fileSize;
}
}
}

View File

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

View File

@@ -1,42 +0,0 @@
using System.IO;
namespace YooAsset
{
internal class VerifyFileElement
{
public string PackageName { private set; get; }
public string BundleGUID { private set; get; }
public string FileRootPath { private set; get; }
public string DataFilePath { private set; get; }
public string InfoFilePath { private set; get; }
public uint DataFileCRC;
public long DataFileSize;
/// <summary>
/// 注意:原子操作对象
/// </summary>
public volatile int Result = 0;
public VerifyFileElement(string packageName, string bundleGUID, string fileRootPath, string dataFilePath, string infoFilePath)
{
PackageName = packageName;
BundleGUID = bundleGUID;
FileRootPath = fileRootPath;
DataFilePath = dataFilePath;
InfoFilePath = infoFilePath;
}
public void DeleteFiles()
{
try
{
Directory.Delete(FileRootPath, true);
}
catch (System.Exception ex)
{
YooLogger.Warning($"Failed to delete cache bundle folder : {ex}");
}
}
}
}

View File

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

View File

@@ -1,138 +0,0 @@

namespace YooAsset
{
internal class DCFSInitializeOperation : FSInitializeFileSystemOperation
{
private enum ESteps
{
None,
CheckAppFootPrint,
SearchCacheFiles,
VerifyCacheFiles,
CreateDownloadScheduler,
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 InternalStart()
{
#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 InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckAppFootPrint)
{
var appFootPrint = new ApplicationFootPrint(_fileSystem);
appFootPrint.Load(_fileSystem.PackageName);
// 如果水印发生变化,则说明覆盖安装后首次打开游戏
if (appFootPrint.IsDirty())
{
if (_fileSystem.InstallClearMode == EOverwriteInstallClearMode.None)
{
YooLogger.Warning("Do nothing when overwrite install application !");
}
else if (_fileSystem.InstallClearMode == EOverwriteInstallClearMode.ClearAllCacheFiles)
{
_fileSystem.DeleteAllBundleFiles();
_fileSystem.DeleteAllManifestFiles();
YooLogger.Warning("Delete all cache files when overwrite install application !");
}
else if (_fileSystem.InstallClearMode == EOverwriteInstallClearMode.ClearAllBundleFiles)
{
_fileSystem.DeleteAllBundleFiles();
YooLogger.Warning("Delete all bundle files when overwrite install application !");
}
else if (_fileSystem.InstallClearMode == EOverwriteInstallClearMode.ClearAllManifestFiles)
{
_fileSystem.DeleteAllManifestFiles();
YooLogger.Warning("Delete all manifest files when overwrite install application !");
}
else
{
throw new System.NotImplementedException(_fileSystem.InstallClearMode.ToString());
}
appFootPrint.Coverage(_fileSystem.PackageName);
}
_steps = ESteps.SearchCacheFiles;
}
if (_steps == ESteps.SearchCacheFiles)
{
if (_searchCacheFilesOp == null)
{
_searchCacheFilesOp = new SearchCacheFilesOperation(_fileSystem);
_searchCacheFilesOp.StartOperation();
AddChildOperation(_searchCacheFilesOp);
}
_searchCacheFilesOp.UpdateOperation();
Progress = _searchCacheFilesOp.Progress;
if (_searchCacheFilesOp.IsDone == false)
return;
_steps = ESteps.VerifyCacheFiles;
}
if (_steps == ESteps.VerifyCacheFiles)
{
if (_verifyCacheFilesOp == null)
{
_verifyCacheFilesOp = new VerifyCacheFilesOperation(_fileSystem, _searchCacheFilesOp.Result);
_verifyCacheFilesOp.StartOperation();
AddChildOperation(_verifyCacheFilesOp);
}
_verifyCacheFilesOp.UpdateOperation();
Progress = _verifyCacheFilesOp.Progress;
if (_verifyCacheFilesOp.IsDone == false)
return;
if (_verifyCacheFilesOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CreateDownloadScheduler;
YooLogger.Log($"Package '{_fileSystem.PackageName}' '{_fileSystem.GetType().Name}' cached files count : {_fileSystem.FileCount}");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyCacheFilesOp.Error;
}
}
if (_steps == ESteps.CreateDownloadScheduler)
{
// 注意:下载中心作为独立任务运行!
if (_fileSystem.DownloadScheduler == null)
{
_fileSystem.DownloadScheduler = new DownloadSchedulerOperation(_fileSystem);
OperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.DownloadScheduler);
}
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}

View File

@@ -1,432 +0,0 @@
using System;
using System.IO;
using UnityEngine;
namespace YooAsset
{
internal class DCFSLoadAssetBundleOperation : FSLoadBundleOperation
{
protected enum ESteps
{
None,
CheckExist,
DownloadFile,
AbortDownload,
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 InternalStart()
{
_steps = ESteps.CheckExist;
}
internal override void InternalUpdate()
{
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
{
if (_fileSystem.DisableOnDemandDownload)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"The bundle not cached : {_bundle.BundleName}";
YooLogger.Warning(Error);
}
else
{
_steps = ESteps.DownloadFile;
}
}
}
if (_steps == ESteps.DownloadFile)
{
// 中断下载
if (AbortDownloadFile)
{
if (_downloadFileOp != null)
_downloadFileOp.AbortOperation();
_steps = ESteps.AbortDownload;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue);
_downloadFileOp = _fileSystem.DownloadFileAsync(_bundle, options);
_downloadFileOp.StartOperation();
AddChildOperation(_downloadFileOp);
}
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
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.AbortDownload)
{
if (_downloadFileOp != null)
{
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Abort download file !";
}
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)
{
var decryptResult = _fileSystem.LoadEncryptedAssetBundleFallback(_bundle);
_assetBundle = decryptResult.Result;
if (_assetBundle != null)
{
_steps = ESteps.Done;
Result = new AssetBundleResult(_fileSystem, _bundle, _assetBundle, _managedStream);
Status = EOperationStatus.Succeed;
return;
}
else
{
_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())
{
_steps = ESteps.Done;
break;
}
}
}
}
internal class DCFSLoadRawBundleOperation : FSLoadBundleOperation
{
protected enum ESteps
{
None,
CheckExist,
DownloadFile,
AbortDownload,
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 InternalStart()
{
_steps = ESteps.CheckExist;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckExist)
{
if (_fileSystem.Exists(_bundle))
{
// 注意:缓存的原生文件的格式,可能会在业务端根据需求发生变动!
// 注意:这里需要校验文件格式,如果不一致对本地文件进行修正!
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
if (File.Exists(filePath) == false)
{
try
{
var recordFileElement = _fileSystem.GetRecordFileElement(_bundle);
File.Move(recordFileElement.DataFilePath, filePath);
_steps = ESteps.LoadCacheRawBundle;
}
catch (Exception ex)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Faild rename raw data file : {ex.Message}";
}
}
else
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.LoadCacheRawBundle;
}
}
else
{
_steps = ESteps.DownloadFile;
}
}
if (_steps == ESteps.DownloadFile)
{
// 中断下载
if (AbortDownloadFile)
{
if (_downloadFileOp != null)
_downloadFileOp.AbortOperation();
_steps = ESteps.AbortDownload;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue);
_downloadFileOp = _fileSystem.DownloadFileAsync(_bundle, options);
_downloadFileOp.StartOperation();
AddChildOperation(_downloadFileOp);
}
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
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.AbortDownload)
{
if (_downloadFileOp != null)
{
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Abort download file !";
}
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())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

@@ -1,67 +0,0 @@
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 InternalStart()
{
_steps = ESteps.GetAllCacheFiles;
}
internal override void InternalUpdate()
{
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 (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

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

View File

@@ -1,63 +0,0 @@
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 InternalStart()
{
_steps = ESteps.ClearAllCacheFiles;
}
internal override void InternalUpdate()
{
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

@@ -1,142 +0,0 @@
using System.Collections.Generic;
namespace YooAsset
{
internal class ClearCacheBundleFilesByLocationsOperaiton : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
CheckManifest,
CheckArgs,
GetClearCacheFiles,
ClearFilterCacheFiles,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageManifest _manifest;
private readonly object _clearParam;
private string[] _locations;
private List<string> _clearBundleGUIDs;
private int _clearFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearCacheBundleFilesByLocationsOperaiton(DefaultCacheFileSystem fileSystem, PackageManifest manifest, object clearParam)
{
_fileSystem = fileSystem;
_manifest = manifest;
_clearParam = clearParam;
}
internal override void InternalStart()
{
_steps = ESteps.CheckManifest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckManifest)
{
if (_manifest == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Can not found active package manifest !";
}
else
{
_steps = ESteps.CheckArgs;
}
}
if (_steps == ESteps.CheckArgs)
{
if (_clearParam == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Clear param is null !";
return;
}
if (_clearParam is string)
{
_locations = new string[] { _clearParam as string };
}
else if (_clearParam is List<string>)
{
var tempList = _clearParam as List<string>;
_locations = tempList.ToArray();
}
else if (_clearParam is string[])
{
_locations = _clearParam as string[];
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Invalid clear param : {_clearParam.GetType().FullName}";
return;
}
_steps = ESteps.GetClearCacheFiles;
}
if (_steps == ESteps.GetClearCacheFiles)
{
_clearBundleGUIDs = GetBundleGUIDsByLocation();
_clearFileTotalCount = _clearBundleGUIDs.Count;
_steps = ESteps.ClearFilterCacheFiles;
}
if (_steps == ESteps.ClearFilterCacheFiles)
{
for (int i = _clearBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _clearBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_clearBundleGUIDs.RemoveAt(i);
if (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> GetBundleGUIDsByLocation()
{
List<string> result = new List<string>(_locations.Length);
foreach (var location in _locations)
{
string assetPath = _manifest.TryMappingToAssetPath(location);
if (_manifest.TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
{
PackageBundle bundle = _manifest.GetMainPackageBundle(packageAsset.BundleID);
if (bundle != null)
{
result.Add(bundle.BundleGUID);
}
}
}
return result;
}
}
}

View File

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

View File

@@ -1,135 +0,0 @@
using System.Collections.Generic;
namespace YooAsset
{
internal class ClearCacheBundleFilesByTagsOperaiton : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
CheckManifest,
CheckArgs,
GetClearCacheFiles,
ClearFilterCacheFiles,
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 InternalStart()
{
_steps = ESteps.CheckManifest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckManifest)
{
if (_manifest == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Can not found active package manifest !";
}
else
{
_steps = ESteps.CheckArgs;
}
}
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.GetClearCacheFiles;
}
if (_steps == ESteps.GetClearCacheFiles)
{
_clearBundleGUIDs = GetBundleGUIDsByTag();
_clearFileTotalCount = _clearBundleGUIDs.Count;
_steps = ESteps.ClearFilterCacheFiles;
}
if (_steps == ESteps.ClearFilterCacheFiles)
{
for (int i = _clearBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _clearBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_clearBundleGUIDs.RemoveAt(i);
if (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> GetBundleGUIDsByTag()
{
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

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

View File

@@ -1,98 +0,0 @@
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal sealed class ClearUnusedCacheBundleFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
CheckManifest,
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 InternalStart()
{
_steps = ESteps.CheckManifest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckManifest)
{
if (_manifest == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Can not found active package manifest !";
}
else
{
_steps = ESteps.GetUnusedCacheFiles;
}
}
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 (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

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

View File

@@ -1,85 +0,0 @@
using System;
using System.IO;
namespace YooAsset
{
internal sealed class ClearUnusedCacheManifestFilesOperation : FSClearCacheFilesOperation
{
private enum ESteps
{
None,
CheckManifest,
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 InternalStart()
{
_steps = ESteps.CheckManifest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckManifest)
{
if (_manifest == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Can not found active package manifest !";
}
else
{
_steps = ESteps.ClearUnusedCacheFiles;
}
}
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

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

View File

@@ -1,161 +0,0 @@
using System.IO;
using UnityEngine;
namespace YooAsset
{
internal class DownloadPackageBundleOperation : FSDownloadFileOperation
{
protected enum ESteps
{
None,
CheckExists,
CreateRequest,
CheckRequest,
TryAgain,
Done,
}
// 下载参数
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly DownloadFileOptions _options;
private DownloadAndCacheFileOperation _downloadFileOp;
protected int _requestCount = 0;
protected float _tryAgainTimer = 0;
protected int _failedTryAgain;
private ESteps _steps = ESteps.None;
internal DownloadPackageBundleOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle)
{
_fileSystem = fileSystem;
_options = options;
_failedTryAgain = options.FailedTryAgain;
}
internal override void InternalStart()
{
_steps = ESteps.CheckExists;
}
internal override void InternalUpdate()
{
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)
{
if (_options.IsValid() == false)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Download file options is invalid !";
Debug.Log(Error);
return;
}
string url = GetRequestURL();
_downloadFileOp = _fileSystem.DownloadScheduler.DownloadAndCacheFileAsync(Bundle, url);
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
Progress = _downloadFileOp.Progress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
DownloadProgress = _downloadFileOp.DownloadProgress;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
if (IsWaitForAsyncComplete == false && _failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_downloadFileOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadFileOp.Error;
YooLogger.Error(Error);
}
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_tryAgainTimer = 0f;
_failedTryAgain--;
Progress = 0f;
DownloadProgress = 0f;
DownloadedBytes = 0;
_steps = ESteps.CreateRequest;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
internal override void InternalAbort()
{
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_downloadFileOp != null)
{
_downloadFileOp.Release();
}
}
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _options.FallbackURL;
else
return _options.MainURL;
}
}
}

View File

@@ -1,93 +0,0 @@
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 IDownloadFileRequest _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 InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(DownloadPackageHashOperation));
_steps = ESteps.CheckExist;
}
internal override void InternalUpdate()
{
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);
int watchdogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadFileRequestArgs(webURL, savePath, _timeout, watchdogTime);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EDownloadRequestStatus.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

@@ -1,93 +0,0 @@
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 IDownloadFileRequest _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 InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(DownloadPackageManifestOperation));
_steps = ESteps.CheckExist;
}
internal override void InternalUpdate()
{
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);
int watchdogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadFileRequestArgs(webURL, savePath, _timeout, watchdogTime);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EDownloadRequestStatus.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

@@ -1,107 +0,0 @@
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 InternalStart()
{
_steps = ESteps.LoadFileData;
}
internal override void InternalUpdate()
{
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)
{
if (ManifestTools.VerifyManifestData(_fileData, _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(_fileSystem.ManifestServices, _fileData);
_deserializer.StartOperation();
AddChildOperation(_deserializer);
}
_deserializer.UpdateOperation();
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;
}
}
}
internal override string InternalGetDesc()
{
return $"PackageVersion : {_packageVersion} PackageHash : {_packageHash}";
}
}
}

View File

@@ -1,100 +0,0 @@

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 IDownloadTextRequest _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 InternalStart()
{
_requestCount = WebRequestCounter.GetRequestFailedCount(_fileSystem.PackageName, nameof(RequestRemotePackageVersionOperation));
_steps = ESteps.RequestPackageVersion;
}
internal override void InternalUpdate()
{
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);
int watchDogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadDataRequestArgs(url, _timeout, watchDogTime);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
Progress = _webTextRequestOp.DownloadProgress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EDownloadRequestStatus.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

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

View File

@@ -1,51 +0,0 @@

namespace YooAsset
{
internal abstract class DownloadAndCacheFileOperation : AsyncOperationBase
{
/// <summary>
/// 引用计数
/// </summary>
public int RefCount { private set; get; }
/// <summary>
/// 下载地址
/// </summary>
public readonly string URL;
/// <summary>
/// 下载进度
/// </summary>
public float DownloadProgress { get; protected set; }
/// <summary>
/// 下载字节
/// </summary>
public long DownloadedBytes { get; protected set; }
public DownloadAndCacheFileOperation(string url)
{
URL = url;
}
internal override string InternalGetDesc()
{
return $"RefCount : {RefCount}";
}
/// <summary>
/// 减少引用计数
/// </summary>
public void Release()
{
RefCount--;
}
/// <summary>
/// 增加引用计数
/// </summary>
public void Reference()
{
RefCount++;
}
}
}

View File

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

View File

@@ -1,199 +0,0 @@
using System.IO;
namespace YooAsset
{
internal sealed class DownloadAndCacheLocalFileOperation : DownloadAndCacheFileOperation
{
private enum ESteps
{
None,
CheckCopy,
CopyLocalFile,
CreateRequest,
CheckRequest,
VerifyBundleFile,
CacheBundleFile,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private readonly string _tempFilePath;
private IDownloadRequest _request;
private VerifyTempFileOperation _verifyOperation;
private ESteps _steps = ESteps.None;
internal DownloadAndCacheLocalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url) : base(url)
{
_fileSystem = fileSystem;
_bundle = bundle;
_tempFilePath = _fileSystem.GetTempFilePath(_bundle);
}
internal override void InternalStart()
{
_steps = ESteps.CheckCopy;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件拷贝
if (_steps == ESteps.CheckCopy)
{
// 删除历史缓存文件
FileUtility.CreateFileDirectory(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
if (_fileSystem.CopyLocalFileServices != null)
_steps = ESteps.CopyLocalFile;
else
_steps = ESteps.CreateRequest;
}
// 拷贝本地文件
if (_steps == ESteps.CopyLocalFile)
{
try
{
//TODO 团结引擎,在某些机型(红米),拷贝包内文件会小概率失败!需要借助其它方式来拷贝包内文件。
var localFileInfo = new LocalFileInfo();
localFileInfo.PackageName = _fileSystem.PackageName;
localFileInfo.BundleName = _bundle.BundleName;
localFileInfo.SourceFileURL = URL;
_fileSystem.CopyLocalFileServices.CopyFile(localFileInfo, _tempFilePath);
if (File.Exists(_tempFilePath))
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.VerifyBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed copy local file : {URL}";
}
}
catch (System.Exception ex)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed copy local file : {ex.Message}";
}
}
// 创建下载请求
if (_steps == ESteps.CreateRequest)
{
int watchdogTime = _fileSystem.DownloadWatchDogTime;
int timeout = 0; //注意:文件下载不做超时检测
var args = new DownloadFileRequestArgs(URL, _tempFilePath, timeout, watchdogTime);
_request = _fileSystem.DownloadBackend.CreateFileRequest(args);
_request.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _request.DownloadProgress;
DownloadedBytes = _request.DownloadedBytes;
Progress = DownloadProgress;
if (_request.IsDone == false)
return;
// 检查网络错误
if (_request.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.VerifyBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _request.Error;
}
// 最终释放请求器
_request.Dispose();
}
// 验证下载结果
if (_steps == ESteps.VerifyBundleFile)
{
if (_verifyOperation == null)
{
var element = new TempFileElement(_tempFilePath, _bundle.FileCRC, _bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
_verifyOperation.StartOperation();
AddChildOperation(_verifyOperation);
}
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
_verifyOperation.UpdateOperation();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CacheBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error;
// 注意:验证失败后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
// 缓存文件
if (_steps == ESteps.CacheBundleFile)
{
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 !";
}
// 注意:缓存完成后直接删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
internal override void InternalAbort()
{
if (_request != null)
_request.AbortRequest();
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
//TODO 更新下载后台,防止无限挂起
_fileSystem.DownloadBackend.Update();
//TODO 等待导入或解压本地文件完毕,该操作会挂起主线程!
InternalUpdate();
if (IsDone)
break;
//TODO 短暂休眠避免完全卡死
System.Threading.Thread.Sleep(1);
}
}
}
}

View File

@@ -1,206 +0,0 @@
using System.IO;
namespace YooAsset
{
internal sealed class DownloadAndCacheRemoteFileOperation : DownloadAndCacheFileOperation
{
private enum ESteps
{
None,
CreateRequest,
CheckRequest,
VerifyBundleFile,
CacheBundleFile,
Done,
}
private readonly DefaultCacheFileSystem _fileSystem;
private readonly PackageBundle _bundle;
private readonly string _tempFilePath;
private bool _enableResume = false;
private long _fileOriginLength = 0;
private IDownloadRequest _request;
private VerifyTempFileOperation _verifyOperation;
private ESteps _steps = ESteps.None;
internal DownloadAndCacheRemoteFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url) : base(url)
{
_fileSystem = fileSystem;
_bundle = bundle;
_tempFilePath = _fileSystem.GetTempFilePath(_bundle);
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载请求
if (_steps == ESteps.CreateRequest)
{
FileUtility.CreateFileDirectory(_tempFilePath);
_enableResume = _bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize;
if (_enableResume)
{
_request = CreateResumeRequest();
_request.SendRequest();
_steps = ESteps.CheckRequest;
}
else
{
_request = CreateNormalRequest();
_request.SendRequest();
_steps = ESteps.CheckRequest;
}
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _request.DownloadProgress;
DownloadedBytes = _fileOriginLength + _request.DownloadedBytes;
Progress = DownloadProgress;
if (_request.IsDone == false)
return;
// 检查网络错误
if (_request.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.VerifyBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _request.Error;
}
// 在遇到特殊错误的时候删除文件
if (_enableResume)
ClearTempFileWhenError(_request.HttpCode);
// 最终释放请求器
_request.Dispose();
}
// 验证下载结果
if (_steps == ESteps.VerifyBundleFile)
{
if (_verifyOperation == null)
{
var element = new TempFileElement(_tempFilePath, _bundle.FileCRC, _bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
_verifyOperation.StartOperation();
AddChildOperation(_verifyOperation);
}
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
_verifyOperation.UpdateOperation();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CacheBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error;
// 注意:验证失败后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
// 缓存文件
if (_steps == ESteps.CacheBundleFile)
{
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 !";
}
// 注意:缓存完成后直接删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
internal override void InternalAbort()
{
if (_request != null)
_request.AbortRequest();
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
// 注意:不中断下载任务,保持后台继续下载
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote : {URL} !");
}
}
private IDownloadRequest CreateResumeRequest()
{
// 获取下载起始位置
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= _bundle.FileSize)
{
File.Delete(_tempFilePath);
}
else
{
_fileOriginLength = fileInfo.Length;
}
}
int watchdogTime = _fileSystem.DownloadWatchDogTime;
int timeout = 0; //注意:文件下载不做超时检测
bool appendToFile = true;
bool removeFileOnAbort = false;
long resumeFromBytes = _fileOriginLength;
var args = new DownloadFileRequestArgs(URL, _tempFilePath, timeout, watchdogTime, appendToFile, removeFileOnAbort, resumeFromBytes);
return _fileSystem.DownloadBackend.CreateFileRequest(args);
}
private IDownloadRequest CreateNormalRequest()
{
// 删除历史缓存文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
int watchdogTime = _fileSystem.DownloadWatchDogTime;
int timeout = 0; //注意:文件下载不做超时检测
var args = new DownloadFileRequestArgs(URL, _tempFilePath, timeout, watchdogTime);
return _fileSystem.DownloadBackend.CreateFileRequest(args);
}
private void ClearTempFileWhenError(long httpCode)
{
if (_fileSystem.ResumeDownloadResponseCodes == null)
return;
//说明:如果遇到以下错误返回码,验证失败直接删除文件
if (_fileSystem.ResumeDownloadResponseCodes.Contains(httpCode))
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
}
}

View File

@@ -1,189 +0,0 @@
using System;
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// 下载调度器
/// </summary>
/// <remarks>
/// 管理所有活跃的下载任务,控制并发数量。
/// </remarks>
internal class DownloadSchedulerOperation : AsyncOperationBase, IDisposable
{
private readonly DefaultCacheFileSystem _fileSystem;
private readonly Dictionary<string, DownloadAndCacheFileOperation> _downloaders = new Dictionary<string, DownloadAndCacheFileOperation>(1000);
private readonly List<string> _removeList = new List<string>(1000);
/// <summary>
/// 是否已暂停
/// </summary>
public bool Paused { get; private set; } = false;
/// <summary>
/// 当前活跃的下载任务数
/// </summary>
public int ActiveDownloadCount { get; private set; }
/// <summary>
/// 当前等待中的下载任务数
/// </summary>
public int PendingDownloadCount
{
get
{
return _downloaders.Count - ActiveDownloadCount;
}
}
/// <summary>
/// 构造下载中心
/// </summary>
public DownloadSchedulerOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
}
internal override void InternalUpdate()
{
// 驱动下载后台
_fileSystem.DownloadBackend.Update();
// 获取可移除的下载器集合
_removeList.Clear();
foreach (var valuePair in _downloaders)
{
var downloader = valuePair.Value;
downloader.UpdateOperation();
if (downloader.IsDone)
{
_removeList.Add(valuePair.Key);
continue;
}
// 注意:主动终止引用计数为零的下载任务
if (downloader.RefCount <= 0)
{
_removeList.Add(valuePair.Key);
downloader.AbortOperation();
continue;
}
}
// 移除下载器
foreach (var key in _removeList)
{
if (_downloaders.TryGetValue(key, out var downloader))
{
RemoveChildOperation(downloader);
_downloaders.Remove(key);
}
}
// 暂停时不启动新任务
if (Paused)
return;
// 最大并发数检测
ActiveDownloadCount = GetProcessingOperationCount();
if (ActiveDownloadCount != _downloaders.Count)
{
int maxConcurrency = _fileSystem.DownloadMaxConcurrency;
int maxRequestPerFrame = _fileSystem.DownloadMaxRequestPerFrame;
if (ActiveDownloadCount < maxConcurrency)
{
int startCount = maxConcurrency - ActiveDownloadCount;
if (startCount > maxRequestPerFrame)
startCount = maxRequestPerFrame;
foreach (var operationPair in _downloaders)
{
var operation = operationPair.Value;
if (operation.Status == EOperationStatus.None)
{
operation.StartOperation();
startCount--;
if (startCount <= 0)
break;
}
}
}
}
}
internal override string InternalGetDesc()
{
return $"{_fileSystem.GetType().FullName}";
}
/// <summary>
/// 中止所有下载任务
/// </summary>
public void AbortAll()
{
foreach (var valuePair in _downloaders)
{
valuePair.Value.AbortOperation();
}
_downloaders.Clear();
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
AbortAll();
}
/// <summary>
/// 创建下载任务
/// </summary>
/// <param name="bundle">资源包信息</param>
/// <param name="url">下载地址</param>
/// <returns>下载操作</returns>
public DownloadAndCacheFileOperation DownloadAndCacheFileAsync(PackageBundle bundle, string url)
{
// 查询旧的下载器
if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader))
{
oldDownloader.Reference();
return oldDownloader;
}
// 创建新的下载器
DownloadAndCacheFileOperation newDownloader;
bool isRequestLocalFile = DownloadSystemHelper.IsRequestLocalFile(url);
if (isRequestLocalFile)
{
newDownloader = new DownloadAndCacheLocalFileOperation(_fileSystem, bundle, url);
}
else
{
newDownloader = new DownloadAndCacheRemoteFileOperation(_fileSystem, bundle, url);
}
AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader);
newDownloader.Reference();
return newDownloader;
}
/// <summary>
/// 获取正在进行中的下载器总数
/// </summary>
private int GetProcessingOperationCount()
{
int count = 0;
foreach (var operationPair in _downloaders)
{
var operation = operationPair.Value;
if (operation.Status != EOperationStatus.None)
count++;
}
return count;
}
}
}

View File

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

View File

@@ -1,124 +0,0 @@
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<string> _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 InternalStart()
{
_steps = ESteps.Prepare;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
string rootDirectory = _fileSystem.GetCacheBundleFilesRoot();
if (Directory.Exists(rootDirectory))
{
var directories = Directory.EnumerateDirectories(rootDirectory);
_filesEnumerator = directories.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 = Directory.EnumerateDirectories(rootFoder);
foreach (var chidDirectory in childDirectories)
{
string bundleGUID = Path.GetFileName(chidDirectory);
if (_fileSystem.IsRecordBundleFile(bundleGUID))
continue;
// 创建验证元素类
string fileRootPath = chidDirectory;
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 (IsBusy)
break;
}
return isFindItem;
}
private string FindDataFileExtension(string directory)
{
string dataFileExtension = string.Empty;
string searchPattern = DefaultCacheFileSystemDefine.BundleDataFileName + "*";
var dataFiles = Directory.EnumerateFiles(directory, searchPattern);
foreach (var filePath in dataFiles)
{
dataFileExtension = Path.GetExtension(filePath);
break;
}
return dataFileExtension;
}
}
}

View File

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

View File

@@ -1,175 +0,0 @@
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 InternalStart()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void InternalUpdate()
{
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}");
int threads = Math.Min(workerThreads, ioThreads);
_verifyMaxNum = Math.Min(threads, _fileSystem.FileVerifyMaxConcurrency);
_verifyTotalCount = fileCount;
if (_verifyMaxNum < 1)
_verifyMaxNum = 1;
YooLogger.Log($"Verify max concurrency : {_verifyMaxNum}");
_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 (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

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

View File

@@ -1,91 +0,0 @@
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 InternalStart()
{
_steps = ESteps.VerifyFile;
}
internal override void InternalUpdate()
{
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 等待子线程验证文件完毕,该操作会挂起主线程!
InternalUpdate();
if (IsDone)
break;
//TODO 短暂休眠避免完全卡死
System.Threading.Thread.Sleep(1);
}
}
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

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

View File

@@ -1,791 +0,0 @@
# DefaultCacheFileSystem 缓存文件系统
## 模块概述
DefaultCacheFileSystem 是 YooAsset 的**缓存文件系统**负责管理从远程服务器下载并缓存到本地沙盒的资源文件。该文件系统是联机运行模式HostPlayMode的核心组件提供完整的下载、验证、缓存和加载功能。
### 核心特性
- **智能缓存管理**:基于 GUID 的文件索引,支持增量更新
- **断点续传**:大文件下载支持从断点继续
- **多线程验证**:后台线程验证文件完整性,不阻塞主线程
- **并发下载**:可配置的下载并发数和请求速率
- **覆盖安装检测**App 版本变更时自动清理过期缓存
- **加密支持**:支持加密资源包的解密加载
---
## 设计目标
| 目标 | 说明 |
|------|------|
| **高性能** | 多线程验证、并发下载、路径缓存优化 |
| **高可靠** | CRC/Hash 验证、损坏文件自动清理、加载失败重试 |
| **可扩展** | 支持自定义解密服务、远程服务、本地拷贝服务 |
| **易配置** | 丰富的参数配置,适应不同网络环境 |
---
## 文件结构
```
DefaultCacheFileSystem/
├── DefaultCacheFileSystem.cs # 文件系统主类
├── DefaultCacheFileSystemDefine.cs # 常量定义
├── EOverwriteInstallClearMode.cs # 覆盖安装清理模式枚举
├── ApplicationFootPrint.cs # 应用版本足迹
├── Elements/ # 元素类
│ ├── RecordFileElement.cs # 缓存文件记录元素
│ ├── TempFileElement.cs # 临时文件元素
│ └── VerifyFileElement.cs # 验证文件元素
└── Operation/ # 操作类
├── DCFSInitializeOperation.cs # 初始化操作
├── DCFSRequestPackageVersionOperation.cs # 请求版本操作
├── DCFSLoadPackageManifestOperation.cs # 加载清单操作
├── DCFSLoadBundleOperation.cs # 加载资源包操作
└── internal/ # 内部操作类
├── SearchCacheFilesOperation.cs # 搜索缓存文件
├── VerifyCacheFilesOperation.cs # 验证缓存文件
├── VerifyTempFileOperation.cs # 验证临时文件
├── DownloadPackageHashOperation.cs # 下载哈希文件
├── DownloadPackageManifestOperation.cs # 下载清单文件
├── DownloadPackageBundleOperation.cs # 下载资源包
├── LoadCachePackageHashOperation.cs # 加载缓存哈希
├── LoadCachePackageManifestOperation.cs # 加载缓存清单
├── ClearAllCacheBundleFilesOperation.cs # 清理所有缓存
├── ClearUnusedCacheBundleFilesOperation.cs # 清理未使用缓存
├── ClearCacheBundleFilesByTagsOperaiton.cs # 按标签清理
├── ClearCacheBundleFilesByLocationsOperaiton.cs # 按位置清理
├── ClearAllCacheManifestFilesOperation.cs # 清理所有清单
├── ClearUnusedCacheManifestFilesOperation.cs # 清理未使用清单
└── Scheduler/ # 下载调度器
├── DownloadSchedulerOperation.cs # 下载调度器
├── DownloadAndCacheFileOperation.cs # 下载并缓存基类
├── DownloadAndCacheRemoteFileOperation.cs # 远程文件下载
└── DownloadAndCacheLocalFileOperation.cs # 本地文件拷贝
```
---
## 核心类说明
### DefaultCacheFileSystem
缓存文件系统的主类,实现 `IFileSystem` 接口。
#### 基本属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `PackageName` | `string` | 包裹名称 |
| `FileRoot` | `string` | 缓存根目录 |
| `FileCount` | `int` | 已缓存文件数量 |
| `DownloadBackend` | `IDownloadBackend` | 下载后台接口 |
| `DownloadScheduler` | `DownloadSchedulerOperation` | 下载调度器 |
#### 自定义参数
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `RemoteServices` | `IRemoteServices` | - | 远程服务接口(必需) |
| `InstallClearMode` | `EOverwriteInstallClearMode` | `ClearAllManifestFiles` | 覆盖安装缓存清理模式 |
| `FileVerifyLevel` | `EFileVerifyLevel` | `Middle` | 初始化时文件校验级别 |
| `FileVerifyMaxConcurrency` | `int` | `32` | 文件校验最大并发数1-256 |
| `AppendFileExtension` | `bool` | `false` | 数据文件追加文件扩展名 |
| `DisableOnDemandDownload` | `bool` | `false` | 禁用边玩边下机制 |
| `DownloadMaxConcurrency` | `int` | `10` | 最大并发下载数1-64 |
| `DownloadMaxRequestPerFrame` | `int` | `5` | 每帧最大请求数1-20 |
| `DownloadWatchDogTime` | `int` | `0` | 下载看门狗超时时间(秒) |
| `ResumeDownloadMinimumSize` | `long` | `long.MaxValue` | 启用断点续传的最小文件大小 |
| `ResumeDownloadResponseCodes` | `List<long>` | `null` | 断点续传关注的HTTP错误码 |
| `DecryptionServices` | `IDecryptionServices` | `null` | 解密服务接口 |
| `ManifestServices` | `IManifestRestoreServices` | `null` | 清单服务接口 |
| `CopyLocalFileServices` | `ICopyLocalFileServices` | `null` | 本地文件拷贝服务 |
#### 核心方法
```csharp
// 生命周期
void OnCreate(string packageName, string packageRoot);
void OnDestroy();
void SetParameter(string name, object value);
// 异步操作
FSInitializeFileSystemOperation InitializeFileSystemAsync();
FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout);
FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout);
FSLoadBundleOperation LoadBundleFile(PackageBundle bundle);
FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options);
FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options);
// 文件查询
bool Belong(PackageBundle bundle); // 始终返回 true保底加载
bool Exists(PackageBundle bundle); // 检查文件是否已缓存
bool NeedDownload(PackageBundle bundle); // 检查是否需要下载
bool NeedUnpack(PackageBundle bundle); // 始终返回 false
bool NeedImport(PackageBundle bundle); // 检查是否需要导入
// 文件访问
string GetBundleFilePath(PackageBundle bundle);
byte[] ReadBundleFileData(PackageBundle bundle);
string ReadBundleFileText(PackageBundle bundle);
```
---
## 缓存目录结构
```
{CacheRoot}/{PackageName}/
├── BundleFiles/ # 资源包文件目录
│ ├── {Hash[0:2]}/ # 哈希前两位分组256个目录
│ │ ├── {BundleGUID}/ # 资源包 GUID 目录
│ │ │ ├── __data # 数据文件(或 __data.bundle
│ │ │ └── __info # 信息文件CRC + Size
│ │ └── ...
│ └── ...
├── ManifestFiles/ # 清单文件目录
│ ├── {PackageName}_{Version}.bytes # 清单二进制文件
│ ├── {PackageName}_{Version}.hash # 清单哈希文件
│ └── __app_footprint.txt # 应用版本足迹文件
└── TempFiles/ # 临时文件目录
├── {BundleGUID} # 下载中的临时文件
└── ...
```
### 信息文件格式__info
```
| 字段 | 类型 | 大小 | 说明 |
|------|------|------|------|
| DataFileCRC | uint32 | 4 bytes | 数据文件 CRC |
| DataFileSize | int64 | 8 bytes | 数据文件大小 |
```
---
## 操作类说明
### DCFSInitializeOperation
初始化操作,执行完整的缓存系统初始化流程。
```
状态流程:
CheckAppFootPrint
├── 版本相同 → 继续
└── 版本不同 → 根据 InstallClearMode 清理缓存
SearchCacheFiles
└── SearchCacheFilesOperation
└── 遍历 BundleFiles 目录
└── 收集需要验证的文件
VerifyCacheFiles
└── VerifyCacheFilesOperation多线程
├── 验证成功 → 记录到 _records
└── 验证失败 → 删除损坏文件
CreateDownloadScheduler
└── 创建 DownloadSchedulerOperation
Done → Status = Succeed
```
#### 状态机枚举
```csharp
private enum ESteps
{
None,
CheckAppFootPrint, // 检查应用版本足迹
SearchCacheFiles, // 搜索缓存文件
VerifyCacheFiles, // 验证缓存文件
CreateDownloadScheduler,// 创建下载调度器
Done // 完成
}
```
### DCFSLoadAssetBundleOperation
加载 AssetBundle 操作,支持按需下载和多重容错机制。
```
状态流程:
CheckExist
├── 已缓存 → LoadAssetBundle
└── 未缓存 → 检查 DisableOnDemandDownload
├── 禁用 → Failed
└── 启用 → DownloadFile
DownloadFile
└── DownloadFileAsync()
├── 下载成功 → LoadAssetBundle
└── 下载失败 → Failed
LoadAssetBundle
├── 未加密 → AssetBundle.LoadFromFile[Async]
└── 已加密 → DecryptionServices.LoadAssetBundle[Async]
CheckResult
├── 加载成功 → AssetBundleResult → Succeed
└── 加载失败 → 验证文件完整性
├── 验证通过 → LoadFromMemory 重试
└── 验证失败 → 删除损坏文件 → Failed
```
#### 移动平台容错机制
```csharp
// 注意:在安卓移动平台,华为和三星真机上有极小概率加载资源包失败。
// 说明:大多数情况在首次安装下载资源到沙盒内,游戏过程中切换到后台再回到游戏内有很大概率触发!
string filePath = _fileSystem.GetCacheBundleFileLoadPath(_bundle);
byte[] fileData = FileUtility.ReadAllBytes(filePath);
if (fileData != null && fileData.Length > 0)
{
_assetBundle = AssetBundle.LoadFromMemory(fileData);
// ...
}
```
### DCFSLoadRawBundleOperation
加载原生资源包操作,处理文件格式变更场景。
```csharp
// 注意:缓存的原生文件的格式,可能会在业务端根据需求发生变动!
// 注意:这里需要校验文件格式,如果不一致对本地文件进行修正!
if (File.Exists(filePath) == false)
{
var recordFileElement = _fileSystem.GetRecordFileElement(_bundle);
File.Move(recordFileElement.DataFilePath, filePath);
}
```
---
## 下载调度器
### DownloadSchedulerOperation
管理所有活跃的下载任务,控制并发数量。
#### 核心属性
| 属性 | 类型 | 说明 |
|------|------|------|
| `Paused` | `bool` | 是否已暂停 |
| `ActiveDownloadCount` | `int` | 当前活跃的下载任务数 |
| `PendingDownloadCount` | `int` | 当前等待中的下载任务数 |
#### 工作原理
```
InternalUpdate()
├── 1. 驱动下载后台 _fileSystem.DownloadBackend.Update()
├── 2. 遍历下载器集合
│ ├── 已完成 → 加入移除列表
│ └── RefCount <= 0 → 中止并移除
├── 3. 移除已完成/中止的下载器
└── 4. 启动新下载任务(如未暂停)
├── 计算可启动数量 = min(maxConcurrency - active, maxRequestPerFrame)
└── 启动等待中的下载器
```
#### 引用计数机制
```csharp
// 查询旧的下载器(复用)
if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader))
{
oldDownloader.Reference(); // 引用计数 +1
return oldDownloader;
}
// 创建新的下载器
DownloadAndCacheFileOperation newDownloader;
// ...
newDownloader.Reference(); // 引用计数 +1
```
### DownloadAndCacheRemoteFileOperation
远程文件下载操作,支持断点续传。
```
状态流程:
CreateRequest
├── 文件大小 >= ResumeDownloadMinimumSize
│ └── CreateResumeRequest断点续传
└── 文件大小 < ResumeDownloadMinimumSize
└── CreateNormalRequest普通下载
CheckRequest
├── 下载成功 → VerifyBundleFile
└── 下载失败 → ClearTempFileWhenError → Failed
VerifyBundleFile
└── VerifyTempFileOperation多线程验证
├── 验证成功 → CacheBundleFile
└── 验证失败 → 删除临时文件 → Failed
CacheBundleFile
└── WriteCacheBundleFile()
├── 成功 → 删除临时文件 → Succeed
└── 失败 → Failed
```
#### 断点续传实现
```csharp
private IDownloadRequest CreateResumeRequest()
{
// 获取下载起始位置
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= _bundle.FileSize)
{
File.Delete(_tempFilePath); // 文件已完整,删除重下
}
else
{
_fileOriginLength = fileInfo.Length; // 记录已下载大小
}
}
var args = new DownloadFileRequestArgs(
URL, _tempFilePath, timeout, watchdogTime,
appendToFile: true, // 追加写入
removeFileOnAbort: false, // 中止时保留文件
resumeFromBytes: _fileOriginLength // 断点位置
);
return _fileSystem.DownloadBackend.CreateFileRequest(args);
}
```
---
## 缓存验证系统
### VerifyCacheFilesOperation
多线程缓存文件验证,在初始化时执行。
#### 验证流程
```
InitVerify
├── 获取系统线程池信息
└── 计算实际并发数 = min(threads, FileVerifyMaxConcurrency)
UpdateVerify循环
├── 检测已完成的验证任务
│ ├── 验证成功 → RecordBundleFile
│ └── 验证失败 → DeleteFiles
└── 启动新的验证任务
└── ThreadPool.QueueUserWorkItem(VerifyInThread)
```
#### 验证级别
```csharp
private EFileVerifyResult VerifyingCacheFile(VerifyFileElement element, EFileVerifyLevel verifyLevel)
{
if (verifyLevel == EFileVerifyLevel.Low)
{
// Low仅检查文件存在
if (File.Exists(element.InfoFilePath) == false)
return EFileVerifyResult.InfoFileNotExisted;
if (File.Exists(element.DataFilePath) == false)
return EFileVerifyResult.DataFileNotExisted;
return EFileVerifyResult.Succeed;
}
else
{
// Middle/High检查文件存在 + CRC/Size 验证
_fileSystem.ReadBundleInfoFile(element.InfoFilePath, out element.DataFileCRC, out element.DataFileSize);
return FileVerifyHelper.FileVerify(element.DataFilePath, element.DataFileSize, element.DataFileCRC, verifyLevel);
}
}
```
### VerifyTempFileOperation
下载文件验证,在线程池中执行。
```csharp
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; // 线程安全的结果设置
}
```
---
## 覆盖安装检测
### ApplicationFootPrint
应用版本足迹,用于检测 App 覆盖安装。
```csharp
public static bool IsDirty(DefaultCacheFileSystem fileSystem)
{
string filePath = fileSystem.GetSandboxAppFootPrintFilePath();
if (File.Exists(filePath))
{
string footPrint = FileUtility.ReadAllText(filePath);
return IsValidVersion(footPrint) == false; // 版本不同
}
return true; // 文件不存在
}
```
### EOverwriteInstallClearMode
覆盖安装时的缓存清理模式。
| 枚举值 | 说明 |
|--------|------|
| `NeverClear` | 不清理任何缓存 |
| `ClearAllManifestFiles` | 清理所有清单文件(默认) |
| `ClearAllBundleAndManifestFiles` | 清理所有资源包和清单文件 |
---
## 缓存清理操作
### 清理模式对照表
| 清理模式 | 操作类 | 说明 |
|----------|--------|------|
| `ClearAllBundleFiles` | `ClearAllCacheBundleFilesOperation` | 清理所有缓存资源包 |
| `ClearUnusedBundleFiles` | `ClearUnusedCacheBundleFilesOperation` | 清理不在清单中的资源包 |
| `ClearBundleFilesByTags` | `ClearCacheBundleFilesByTagsOperaiton` | 按标签清理 |
| `ClearBundleFilesByLocations` | `ClearCacheBundleFilesByLocationsOperaiton` | 按资源路径清理 |
| `ClearAllManifestFiles` | `ClearAllCacheManifestFilesOperation` | 清理所有清单文件 |
| `ClearUnusedManifestFiles` | `ClearUnusedCacheManifestFilesOperation` | 清理未使用的清单文件 |
### 时间切片清理
```csharp
for (int i = _allBundleGUIDs.Count - 1; i >= 0; i--)
{
string bundleGUID = _allBundleGUIDs[i];
_fileSystem.DeleteCacheBundleFile(bundleGUID);
_allBundleGUIDs.RemoveAt(i);
// 检查操作系统是否繁忙,避免阻塞主线程
if (OperationSystem.IsBusy)
break;
}
```
---
## 元素类说明
### RecordFileElement
缓存文件记录元素,存储已验证的缓存文件信息。
```csharp
internal class RecordFileElement
{
public readonly string InfoFilePath; // 信息文件路径
public readonly string DataFilePath; // 数据文件路径
public readonly uint DataFileCRC; // 数据文件 CRC
public readonly long DataFileSize; // 数据文件大小
public bool DeleteFolder(); // 删除整个文件夹
}
```
### TempFileElement
临时文件元素,用于下载文件验证。
```csharp
internal class TempFileElement
{
public readonly string TempFilePath; // 临时文件路径
public readonly uint TempFileCRC; // 预期 CRC
public readonly long TempFileSize; // 预期文件大小
private int _result = 0;
public int Result // 线程安全的验证结果
{
get => Interlocked.CompareExchange(ref _result, 0, 0);
set => Interlocked.Exchange(ref _result, value);
}
}
```
### VerifyFileElement
验证文件元素,用于缓存文件批量验证。
```csharp
internal class VerifyFileElement
{
public readonly string PackageName; // 包裹名称
public readonly string BundleGUID; // 资源包 GUID
public readonly string FileRootPath; // 文件根目录
public readonly string DataFilePath; // 数据文件路径
public readonly string InfoFilePath; // 信息文件路径
public uint DataFileCRC; // 数据文件 CRC
public long DataFileSize; // 数据文件大小
private int _result = 0;
public int Result // 线程安全的验证结果
{
get => Interlocked.CompareExchange(ref _result, 0, 0);
set => Interlocked.Exchange(ref _result, value);
}
public void DeleteFiles(); // 删除所有相关文件
}
```
---
## 使用示例
### 基础配置
```csharp
// 创建远程服务接口
class GameRemoteServices : IRemoteServices
{
public string GetRemoteMainURL(string fileName)
{
return $"https://cdn.example.com/bundles/{fileName}";
}
public string GetRemoteFallbackURL(string fileName)
{
return $"https://cdn-backup.example.com/bundles/{fileName}";
}
}
// 创建缓存文件系统参数
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 初始化包裹
var initParams = new HostPlayModeParameters();
initParams.BuildinFileSystemParameters = buildinParams;
initParams.CacheFileSystemParameters = cacheParams;
var initOp = package.InitializeAsync(initParams);
```
### 配置下载参数
```csharp
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 设置下载并发数
cacheParams.AddParameter(FileSystemParametersDefine.DOWNLOAD_MAX_CONCURRENCY, 8);
// 设置每帧最大请求数
cacheParams.AddParameter(FileSystemParametersDefine.DOWNLOAD_MAX_REQUEST_PER_FRAME, 3);
// 设置下载看门狗时间(秒)
cacheParams.AddParameter(FileSystemParametersDefine.DOWNLOAD_WATCH_DOG_TIME, 30);
```
### 配置断点续传
```csharp
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 启用断点续传的最小文件大小1MB
cacheParams.AddParameter(FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE, 1024 * 1024);
// 断点续传关注的HTTP错误码这些错误码时删除临时文件重新下载
var responseCodes = new List<long> { 416 }; // Range Not Satisfiable
cacheParams.AddParameter(FileSystemParametersDefine.RESUME_DOWNLOAD_RESPONSE_CODES, responseCodes);
```
### 配置文件验证
```csharp
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 设置验证级别
cacheParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_LEVEL, EFileVerifyLevel.High);
// 设置验证并发数
cacheParams.AddParameter(FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY, 64);
```
### 配置加密支持
```csharp
// 自定义解密服务
class GameDecryptionServices : IDecryptionServices
{
public DecryptResult LoadAssetBundle(DecryptFileInfo fileInfo)
{
// 实现解密逻辑
byte[] data = DecryptFile(fileInfo.FileLoadPath);
AssetBundle bundle = AssetBundle.LoadFromMemory(data);
return new DecryptResult { Result = bundle };
}
// ...
}
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 设置解密服务
cacheParams.AddParameter(FileSystemParametersDefine.DECRYPTION_SERVICES, new GameDecryptionServices());
```
### 配置覆盖安装行为
```csharp
var cacheParams = FileSystemParameters.CreateDefaultCacheFileSystemParameters(
remoteServices: new GameRemoteServices()
);
// 覆盖安装时清理所有资源包和清单
cacheParams.AddParameter(
FileSystemParametersDefine.INSTALL_CLEAR_MODE,
EOverwriteInstallClearMode.ClearAllBundleAndManifestFiles
);
```
### 清理缓存
```csharp
// 清理所有缓存
var clearOp = package.ClearCacheFilesAsync(EFileClearMode.ClearAllBundleFiles);
await clearOp.ToTask();
// 清理未使用的缓存
var clearOp = package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles);
await clearOp.ToTask();
// 按标签清理
var clearOp = package.ClearCacheFilesAsync(EFileClearMode.ClearBundleFilesByTags, "dlc1");
await clearOp.ToTask();
```
---
## 参数常量
```csharp
// 远程服务
FileSystemParametersDefine.REMOTE_SERVICES // IRemoteServices: 远程服务接口
// 覆盖安装
FileSystemParametersDefine.INSTALL_CLEAR_MODE // EOverwriteInstallClearMode: 清理模式
// 文件验证
FileSystemParametersDefine.FILE_VERIFY_LEVEL // EFileVerifyLevel: 验证级别
FileSystemParametersDefine.FILE_VERIFY_MAX_CONCURRENCY // int: 验证并发数
// 文件格式
FileSystemParametersDefine.APPEND_FILE_EXTENSION // bool: 追加文件扩展名
// 下载控制
FileSystemParametersDefine.DISABLE_ONDEMAND_DOWNLOAD // bool: 禁用边玩边下
FileSystemParametersDefine.DOWNLOAD_MAX_CONCURRENCY // int: 下载并发数
FileSystemParametersDefine.DOWNLOAD_MAX_REQUEST_PER_FRAME // int: 每帧请求数
FileSystemParametersDefine.DOWNLOAD_WATCH_DOG_TIME // int: 看门狗时间
// 断点续传
FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE // long: 最小文件大小
FileSystemParametersDefine.RESUME_DOWNLOAD_RESPONSE_CODES // List<long>: 关注错误码
// 服务接口
FileSystemParametersDefine.DECRYPTION_SERVICES // IDecryptionServices: 解密服务
FileSystemParametersDefine.MANIFEST_SERVICES // IManifestRestoreServices: 清单服务
FileSystemParametersDefine.COPY_LOCAL_FILE_SERVICES // ICopyLocalFileServices: 本地拷贝服务
```
---
## 类继承关系
```
IFileSystem
└── DefaultCacheFileSystem
FSInitializeFileSystemOperation
└── DCFSInitializeOperation
FSRequestPackageVersionOperation
└── DCFSRequestPackageVersionOperation
FSLoadPackageManifestOperation
└── DCFSLoadPackageManifestOperation
FSLoadBundleOperation
├── DCFSLoadAssetBundleOperation
└── DCFSLoadRawBundleOperation
FSDownloadFileOperation
└── DownloadPackageBundleOperation
FSClearCacheFilesOperation
├── ClearAllCacheBundleFilesOperation
├── ClearUnusedCacheBundleFilesOperation
├── ClearCacheBundleFilesByTagsOperaiton
├── ClearCacheBundleFilesByLocationsOperaiton
├── ClearAllCacheManifestFilesOperation
└── ClearUnusedCacheManifestFilesOperation
AsyncOperationBase
├── DownloadSchedulerOperation
├── DownloadAndCacheFileOperation (abstract)
│ ├── DownloadAndCacheRemoteFileOperation
│ └── DownloadAndCacheLocalFileOperation
├── SearchCacheFilesOperation
├── VerifyCacheFilesOperation
├── VerifyTempFileOperation
├── DownloadPackageHashOperation
├── DownloadPackageManifestOperation
├── LoadCachePackageHashOperation
├── LoadCachePackageManifestOperation
└── RequestRemotePackageVersionOperation
BundleResult
├── AssetBundleResult ← AssetBundle 资源
└── RawBundleResult ← 原生文件资源
```
---
## 注意事项
1. **远程服务必需**:使用缓存文件系统必须配置 `IRemoteServices` 接口
2. **保底加载**:缓存文件系统的 `Belong()` 始终返回 true作为资源加载的保底方案
3. **线程安全**:文件验证和下载使用后台线程,但核心逻辑仍在主线程执行
4. **并发限制**:合理设置下载和验证的并发数,避免系统过载
5. **移动平台**Android 平台存在极小概率的 AssetBundle 加载失败,系统会自动尝试 LoadFromMemory 作为备选方案
6. **断点续传**:启用断点续传时,需要合理设置 `ResumeDownloadMinimumSize``ResumeDownloadResponseCodes`
7. **覆盖安装**App 版本更新时会自动检测并根据配置清理缓存,避免旧缓存导致问题

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 8292f707fbf60854e852e1a75824d892
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,268 +0,0 @@
using System;
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// 模拟文件系统
/// </summary>
internal class DefaultEditorFileSystem : IFileSystem
{
protected readonly Dictionary<string, string> _records = new Dictionary<string, string>(10000);
protected string _packageRoot;
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _packageRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return 0;
}
}
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { private set; get; }
/// <summary>
/// 模拟WebGL平台模式
/// </summary>
public bool VirtualWebGLMode { private set; get; } = false;
/// <summary>
/// 模拟虚拟下载模式
/// </summary>
public bool VirtualDownloadMode { private set; get; } = false;
/// <summary>
/// 模拟虚拟下载的网速(单位:字节)
/// </summary>
public int VirtualDownloadSpeed { private set; get; } = 1024;
/// <summary>
/// 异步模拟加载最小帧数
/// </summary>
public int AsyncSimulateMinFrame { private set; get; } = 1;
/// <summary>
/// 异步模拟加载最大帧数
/// </summary>
public int AsyncSimulateMaxFrame { private set; get; } = 1;
#endregion
public DefaultEditorFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new DEFSInitializeOperation(this);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new DEFSLoadPackageManifestOperation(this, packageVersion);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new DEFSRequestPackageVersionOperation(this);
return operation;
}
public virtual FSClearCacheFilesOperation ClearCacheFilesAsync(PackageManifest manifest, ClearCacheFilesOptions options)
{
var operation = new FSClearCacheFilesCompleteOperation();
return operation;
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{
string mainURL = bundle.BundleName;
options.SetURL(mainURL, mainURL);
var downloader = new DownloadVirtualBundleOperation(this, bundle, options);
return downloader;
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (bundle.BundleType == (int)EBuildBundleType.VirtualBundle)
{
var operation = new DEFSLoadBundleOperation(this, bundle);
return operation;
}
else
{
string error = $"{nameof(DefaultEditorFileSystem)} not support load bundle type : {bundle.BundleType}";
var operation = new FSLoadBundleCompleteOperation(error);
return operation;
}
}
public virtual void SetParameter(string name, object value)
{
if (name == FileSystemParametersDefine.DOWNLOAD_BACKEND)
{
DownloadBackend = (IDownloadBackend)value;
}
else if (name == FileSystemParametersDefine.UNITY_WEB_REQUEST_CREATOR)
{
WebRequestCreator = (UnityWebRequestCreator)value;
}
else if (name == FileSystemParametersDefine.VIRTUAL_WEBGL_MODE)
{
VirtualWebGLMode = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.VIRTUAL_DOWNLOAD_MODE)
{
VirtualDownloadMode = Convert.ToBoolean(value);
}
else if (name == FileSystemParametersDefine.VIRTUAL_DOWNLOAD_SPEED)
{
VirtualDownloadSpeed = Convert.ToInt32(value);
}
else if (name == FileSystemParametersDefine.ASYNC_SIMULATE_MIN_FRAME)
{
AsyncSimulateMinFrame = Convert.ToInt32(value);
}
else if (name == FileSystemParametersDefine.ASYNC_SIMULATE_MAX_FRAME)
{
AsyncSimulateMaxFrame = Convert.ToInt32(value);
}
else
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
if (string.IsNullOrEmpty(packageRoot))
throw new YooFileSystemException($"{nameof(DefaultEditorFileSystem)} package root is null or empty !");
_packageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
}
public virtual void OnDestroy()
{
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
public virtual bool Belong(PackageBundle bundle)
{
return true;
}
public virtual bool Exists(PackageBundle bundle)
{
if (VirtualDownloadMode)
{
return _records.ContainsKey(bundle.BundleGUID);
}
else
{
return true;
}
}
public virtual bool NeedDownload(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
return Exists(bundle) == false;
}
public virtual bool NeedUnpack(PackageBundle bundle)
{
return false;
}
public virtual bool NeedImport(PackageBundle bundle)
{
return false;
}
public virtual string GetBundleFilePath(PackageBundle bundle)
{
if (bundle.IncludeMainAssets.Count == 0)
return string.Empty;
var pacakgeAsset = bundle.IncludeMainAssets[0];
return pacakgeAsset.AssetPath;
}
public virtual byte[] ReadBundleFileData(PackageBundle bundle)
{
if (bundle.IncludeMainAssets.Count == 0)
return null;
var pacakgeAsset = bundle.IncludeMainAssets[0];
return FileUtility.ReadAllBytes(pacakgeAsset.AssetPath);
}
public virtual string ReadBundleFileText(PackageBundle bundle)
{
if (bundle.IncludeMainAssets.Count == 0)
return null;
var pacakgeAsset = bundle.IncludeMainAssets[0];
return FileUtility.ReadAllText(pacakgeAsset.AssetPath);
}
#region
public void RecordDownloadFile(PackageBundle bundle)
{
if (_records.ContainsKey(bundle.BundleGUID) == false)
_records.Add(bundle.BundleGUID, bundle.BundleName);
}
public string GetEditorPackageVersionFilePath()
{
string fileName = YooAssetSettingsData.GetPackageVersionFileName(PackageName);
return PathUtility.Combine(_packageRoot, fileName);
}
public string GetEditorPackageHashFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
public string GetEditorPackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_packageRoot, fileName);
}
public int GetAsyncSimulateFrame()
{
if (AsyncSimulateMinFrame > AsyncSimulateMaxFrame)
{
AsyncSimulateMinFrame = AsyncSimulateMaxFrame;
}
return UnityEngine.Random.Range(AsyncSimulateMinFrame, AsyncSimulateMaxFrame + 1);
}
#endregion
}
}

View File

@@ -1,7 +0,0 @@

namespace YooAsset
{
internal class DefaultEditorFileSystemDefine
{
}
}

View File

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

View File

@@ -1,20 +0,0 @@

namespace YooAsset
{
internal class DEFSInitializeOperation : FSInitializeFileSystemOperation
{
private readonly DefaultEditorFileSystem _fileSytem;
internal DEFSInitializeOperation(DefaultEditorFileSystem fileSystem)
{
_fileSytem = fileSystem;
}
internal override void InternalStart()
{
Status = EOperationStatus.Succeed;
}
internal override void InternalUpdate()
{
}
}
}

View File

@@ -1,155 +0,0 @@

namespace YooAsset
{
internal class DEFSLoadBundleOperation : FSLoadBundleOperation
{
protected enum ESteps
{
None,
CheckExist,
DownloadFile,
AbortDownload,
LoadAssetBundle,
CheckResult,
Done,
}
private readonly DefaultEditorFileSystem _fileSystem;
private readonly PackageBundle _bundle;
protected FSDownloadFileOperation _downloadFileOp;
private int _asyncSimulateFrame;
private ESteps _steps = ESteps.None;
internal DEFSLoadBundleOperation(DefaultEditorFileSystem fileSystem, PackageBundle bundle)
{
_fileSystem = fileSystem;
_bundle = bundle;
}
internal override void InternalStart()
{
_steps = ESteps.CheckExist;
_asyncSimulateFrame = _fileSystem.GetAsyncSimulateFrame();
}
internal override void InternalUpdate()
{
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 (AbortDownloadFile)
{
if (_downloadFileOp != null)
_downloadFileOp.AbortOperation();
_steps = ESteps.AbortDownload;
}
}
if (_steps == ESteps.DownloadFile)
{
if (_downloadFileOp == null)
{
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue);
_downloadFileOp = _fileSystem.DownloadFileAsync(_bundle, options);
_downloadFileOp.StartOperation();
AddChildOperation(_downloadFileOp);
}
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
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.AbortDownload)
{
if (_downloadFileOp != null)
{
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
_downloadFileOp.UpdateOperation();
if (_downloadFileOp.IsDone == false)
return;
}
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Abort download file !";
}
if (_steps == ESteps.LoadAssetBundle)
{
if (IsWaitForAsyncComplete)
{
if (_fileSystem.VirtualWebGLMode)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Virtual WebGL Mode only support asyn load method !";
YooLogger.Error(Error);
}
else
{
_steps = ESteps.CheckResult;
}
}
else
{
if (_asyncSimulateFrame <= 0)
_steps = ESteps.CheckResult;
else
_asyncSimulateFrame--;
}
}
if (_steps == ESteps.CheckResult)
{
_steps = ESteps.Done;
Result = new VirtualBundleResult(_fileSystem, _bundle);
Status = EOperationStatus.Succeed;
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
}
}

View File

@@ -1,148 +0,0 @@
using UnityEngine;
namespace YooAsset
{
internal class DownloadVirtualBundleOperation : FSDownloadFileOperation
{
protected enum ESteps
{
None,
CheckExists,
CreateRequest,
CheckRequest,
TryAgain,
Done,
}
// 下载参数
protected readonly DefaultEditorFileSystem _fileSystem;
protected readonly DownloadFileOptions _options;
protected IDownloadFileRequest _downloadFileOp;
protected int _requestCount = 0;
protected float _tryAgainTimer = 0;
protected int _failedTryAgain;
private ESteps _steps = ESteps.None;
internal DownloadVirtualBundleOperation(DefaultEditorFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle)
{
_fileSystem = fileSystem;
_options = options;
_failedTryAgain = options.FailedTryAgain;
}
internal override void InternalStart()
{
_steps = ESteps.CheckExists;
}
internal override void InternalUpdate()
{
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)
{
if (_options.IsValid() == false)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Download file options is invalid !";
Debug.Log(Error);
return;
}
string url = GetRequestURL();
int speed = _fileSystem.VirtualDownloadSpeed;
var args = new DownloadSimulateRequestArgs(url, Bundle.FileSize, speed);
_downloadFileOp = _fileSystem.DownloadBackend.CreateSimulateRequest(args);
_downloadFileOp.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
Progress = _downloadFileOp.DownloadProgress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
DownloadProgress = _downloadFileOp.DownloadProgress;
if (_downloadFileOp.IsDone == false)
return;
if (_downloadFileOp.Status == EDownloadRequestStatus.Succeed)
{
_fileSystem.RecordDownloadFile(Bundle);
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
if (IsWaitForAsyncComplete == false && _failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_downloadFileOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloadFileOp.Error;
YooLogger.Error(Error);
}
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_tryAgainTimer = 0f;
_failedTryAgain--;
Progress = 0f;
DownloadProgress = 0f;
DownloadedBytes = 0;
_steps = ESteps.CreateRequest;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Try load bundle {Bundle.BundleName} from remote !";
YooLogger.Error(Error);
}
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _options.FallbackURL;
else
return _options.MainURL;
}
}
}

View File

@@ -1,56 +0,0 @@
using System.IO;
namespace YooAsset
{
internal class LoadEditorPackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
LoadHash,
Done,
}
private readonly DefaultEditorFileSystem _fileSystem;
private readonly string _packageVersion;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
internal LoadEditorPackageHashOperation(DefaultEditorFileSystem fileSystem, string packageVersion)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
}
internal override void InternalStart()
{
_steps = ESteps.LoadHash;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadHash)
{
string hashFilePath = _fileSystem.GetEditorPackageHashFilePath(_packageVersion);
if (File.Exists(hashFilePath))
{
_steps = ESteps.Done;
PackageHash = FileUtility.ReadAllText(hashFilePath);
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Can not found simulation package hash file : {hashFilePath}";
}
}
}
}
}

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