feat : add WebNetwork file system

This commit is contained in:
何冠峰
2026-05-26 14:08:04 +08:00
parent 9c05f3514e
commit d337a86e68
50 changed files with 2081 additions and 306 deletions

View File

@@ -50,7 +50,8 @@ namespace YooAsset
watchdogTimeout: _options.WatchdogTimeout, watchdogTimeout: _options.WatchdogTimeout,
disableUnityWebCache: _options.DisableUnityWebCache, disableUnityWebCache: _options.DisableUnityWebCache,
fileHash: _options.Bundle.FileHash, fileHash: _options.Bundle.FileHash,
unityCrc: _options.Bundle.UnityCrc); unityCrc: _options.Bundle.UnityCrc,
platformStrategy : null);
_downloadAssetBundleRequest = _options.DownloadBackend.CreateAssetBundleRequest(args); _downloadAssetBundleRequest = _options.DownloadBackend.CreateAssetBundleRequest(args);
_downloadAssetBundleRequest.SendRequest(); _downloadAssetBundleRequest.SendRequest();
_steps = ESteps.CheckRequest; _steps = ESteps.CheckRequest;
@@ -125,216 +126,4 @@ namespace YooAsset
} }
} }
/// <summary>
/// 从网络加载加密的 AssetBundle 操作
/// </summary>
internal sealed class LoadWebEncryptedAssetBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
Prepare,
DataRequest,
CheckRequest,
VerifyData,
LoadBundle,
CheckResult,
TryAgain,
Done,
}
private readonly LoadWebAssetBundleOptions _options;
private readonly DownloadRetryController _downloadRetryController;
private IDownloadBytesRequest _downloadBytesRequest;
private IBundleMemoryDecryptor _decryptor;
private AssetBundleCreateRequest _createRequest;
private ESteps _steps = ESteps.None;
/// <summary>
/// 创建网络 AssetBundle 加载操作实例
/// </summary>
/// <param name="options">从网络加载 AssetBundle 的操作选项</param>
public LoadWebEncryptedAssetBundleOperation(LoadWebAssetBundleOptions options)
{
_options = options;
// 注意:网络原因失败后,重新尝试直到成功
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
}
protected override void InternalStart()
{
_steps = ESteps.Prepare;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
var decryptor = _options.AssetBundleDecryptor;
if (decryptor == null)
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} asset bundle decryptor is null.");
return;
}
if (decryptor is IBundleMemoryDecryptor)
{
_decryptor = decryptor as IBundleMemoryDecryptor;
_steps = ESteps.DataRequest;
}
else
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
return;
}
}
if (_steps == ESteps.DataRequest)
{
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
var args = new DownloadDataRequestArgs(
url: url,
timeout: 0,
watchdogTimeout: _options.WatchdogTimeout);
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
_downloadBytesRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
if (_steps == ESteps.CheckRequest)
{
Progress = _downloadBytesRequest.DownloadProgress;
if (_downloadBytesRequest.IsDone == false)
return;
if (_downloadBytesRequest.Status == EDownloadRequestStatus.Succeeded)
{
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadBytesRequest.Url);
_steps = ESteps.VerifyData;
}
else
{
string url = _downloadBytesRequest.Url;
long httpCode = _downloadBytesRequest.HttpCode;
string httpError = _downloadBytesRequest.HttpError;
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(_downloadBytesRequest.Error);
YooLogger.LogError(Error);
}
}
}
if (_steps == ESteps.VerifyData)
{
// 注意:网络/代理/服务器异常导致内容不完整但请求仍成功
EFileVerifyResult verifyResult;
if (_options.DownloadVerifyLevel == EFileVerifyLevel.Low || _options.DownloadVerifyLevel == EFileVerifyLevel.Middle)
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, 0);
else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High)
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc);
else
throw new YooInternalException($"Unexpected verify level: {_options.DownloadVerifyLevel}.");
if (verifyResult == EFileVerifyResult.Succeed)
{
_steps = ESteps.LoadBundle;
}
else
{
string error = $"Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
YooLogger.LogWarning(error);
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(error);
}
}
}
if (_steps == ESteps.LoadBundle)
{
LoadResult result = LoadFromMemory(_decryptor, _downloadBytesRequest.Result);
if (result.Succeeded == false)
{
_steps = ESteps.Done;
SetError(result.Error);
return;
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_createRequest.isDone == false)
return;
var assetBundle = _createRequest.assetBundle;
if (assetBundle == null)
{
_steps = ESteps.Done;
SetError("Unity engine load failed.");
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = new AssetBundleHandle(_options.Bundle, assetBundle, null);
}
}
if (_steps == ESteps.TryAgain)
{
// 注意:失败后释放网络请求
if (_downloadBytesRequest != null)
{
_downloadBytesRequest.Dispose();
_downloadBytesRequest = null;
}
if (_downloadRetryController.TickRetryDelay())
{
Progress = 0f;
_steps = ESteps.DataRequest;
}
}
}
protected override void InternalDispose()
{
if (_downloadBytesRequest != null)
{
_downloadBytesRequest.Dispose();
_downloadBytesRequest = null;
}
}
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData)
{
var args = new BundleDecryptArgs(_options.Bundle, fileData, null);
var binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
_createRequest = AssetBundle.LoadFromMemoryAsync(binaryData);
return LoadResult.Default();
}
}
} }

View File

@@ -0,0 +1,213 @@
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// WebGL 平台加载加密 AssetBundle 操作
/// </summary>
internal sealed class LoadWebEncryptedAssetBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
Prepare,
DataRequest,
CheckRequest,
VerifyData,
LoadBundle,
CheckResult,
TryAgain,
Done,
}
private readonly LoadWebEncryptedAssetBundleOptions _options;
private readonly DownloadRetryController _downloadRetryController;
private IDownloadBytesRequest _downloadBytesRequest;
private IBundleMemoryDecryptor _decryptor;
private AssetBundleCreateRequest _createRequest;
private ESteps _steps = ESteps.None;
public LoadWebEncryptedAssetBundleOperation(LoadWebEncryptedAssetBundleOptions options)
{
_options = options;
// 注意:网络原因失败后,重新尝试直到成功
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
}
protected override void InternalStart()
{
_steps = ESteps.Prepare;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Prepare)
{
var decryptor = _options.AssetBundleDecryptor;
if (decryptor == null)
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} asset bundle decryptor is null.");
return;
}
if (decryptor is IBundleMemoryDecryptor)
{
_decryptor = decryptor as IBundleMemoryDecryptor;
_steps = ESteps.DataRequest;
}
else
{
_steps = ESteps.Done;
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
return;
}
}
if (_steps == ESteps.DataRequest)
{
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
var args = new DownloadDataRequestArgs(
url: url,
timeout: 0,
watchdogTimeout: _options.WatchdogTimeout);
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
_downloadBytesRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
if (_steps == ESteps.CheckRequest)
{
Progress = _downloadBytesRequest.DownloadProgress;
if (_downloadBytesRequest.IsDone == false)
return;
if (_downloadBytesRequest.Status == EDownloadRequestStatus.Succeeded)
{
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadBytesRequest.Url);
_steps = ESteps.VerifyData;
}
else
{
string url = _downloadBytesRequest.Url;
long httpCode = _downloadBytesRequest.HttpCode;
string httpError = _downloadBytesRequest.HttpError;
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(_downloadBytesRequest.Error);
YooLogger.LogError(Error);
}
}
}
if (_steps == ESteps.VerifyData)
{
// 注意:网络/代理/服务器异常导致内容不完整但请求仍成功
EFileVerifyResult verifyResult;
if (_options.DownloadVerifyLevel == EFileVerifyLevel.Low || _options.DownloadVerifyLevel == EFileVerifyLevel.Middle)
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, 0);
else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High)
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc);
else
throw new YooInternalException($"Unexpected verify level: {_options.DownloadVerifyLevel}.");
if (verifyResult == EFileVerifyResult.Succeed)
{
_steps = ESteps.LoadBundle;
}
else
{
string error = $"Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
YooLogger.LogWarning(error);
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(error);
}
}
}
if (_steps == ESteps.LoadBundle)
{
LoadResult result = LoadFromMemory(_decryptor, _downloadBytesRequest.Result);
if (result.Succeeded == false)
{
_steps = ESteps.Done;
SetError(result.Error);
return;
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_createRequest.isDone == false)
return;
var assetBundle = _createRequest.assetBundle;
if (assetBundle == null)
{
_steps = ESteps.Done;
SetError("Unity engine load asset bundle failed.");
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = new AssetBundleHandle(_options.Bundle, assetBundle, null);
}
}
if (_steps == ESteps.TryAgain)
{
// 注意:失败后释放网络请求
if (_downloadBytesRequest != null)
{
_downloadBytesRequest.Dispose();
_downloadBytesRequest = null;
}
if (_downloadRetryController.TickRetryDelay())
{
Progress = 0f;
_steps = ESteps.DataRequest;
}
}
}
protected override void InternalDispose()
{
if (_downloadBytesRequest != null)
{
_downloadBytesRequest.Dispose();
_downloadBytesRequest = null;
}
}
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData)
{
var args = new BundleDecryptArgs(_options.Bundle, fileData, null);
var binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
_createRequest = AssetBundle.LoadFromMemoryAsync(binaryData);
return LoadResult.Default();
}
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// WebGL 平台加载加密 AssetBundle 的操作选项
/// </summary>
internal readonly struct LoadWebEncryptedAssetBundleOptions
{
/// <summary>
/// 文件缓存名称
/// </summary>
public string CacheName { get; }
/// <summary>
/// 资源包描述
/// </summary>
public PackageBundle Bundle { get; }
/// <summary>
/// 候选下载地址列表
/// </summary>
public IReadOnlyList<string> CandidateUrls { get; }
/// <summary>
/// AssetBundle 解密器
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; }
/// <summary>
/// 下载数据校验级别
/// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; }
/// <summary>
/// 看门狗超时时间
/// </summary>
public int WatchdogTimeout { get; }
/// <summary>
/// 下载重试判定策略
/// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary>
/// URL 选择策略
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public LoadWebEncryptedAssetBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls,
IBundleDecryptor assetBundleDecryptor, IDownloadBackend downloadBackend, EFileVerifyLevel downloadVerifyLevel,
int watchdogTimeout, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
CacheName = cacheName;
Bundle = bundle;
CandidateUrls = candidateUrls;
AssetBundleDecryptor = assetBundleDecryptor;
DownloadBackend = downloadBackend;
DownloadVerifyLevel = downloadVerifyLevel;
WatchdogTimeout = watchdogTimeout;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
}
}

View File

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

View File

@@ -0,0 +1,123 @@
namespace YooAsset
{
/// <summary>
/// WebGL 平台加载非加密 AssetBundle 操作
/// </summary>
internal sealed class LoadWebPlatformAssetBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
BundleRequest,
CheckRequest,
TryAgain,
Done,
}
private readonly LoadWebPlatformAssetBundleOptions _options;
private readonly DownloadRetryController _downloadRetryController;
private IDownloadAssetBundleRequest _downloadAssetBundleRequest;
private ESteps _steps = ESteps.None;
internal LoadWebPlatformAssetBundleOperation(LoadWebPlatformAssetBundleOptions options)
{
_options = options;
// 注意:网络原因失败后,重新尝试直到成功
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
}
protected override void InternalStart()
{
_steps = ESteps.BundleRequest;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.BundleRequest)
{
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
var args = new DownloadAssetBundleRequestArgs(
url: url,
timeout: 0,
watchdogTimeout: _options.WatchdogTimeout,
disableUnityWebCache: _options.DisableUnityWebCache,
fileHash: _options.Bundle.FileHash,
unityCrc: _options.Bundle.UnityCrc,
platformStrategy: _options.PlatformStrategy);
_downloadAssetBundleRequest = _options.DownloadBackend.CreateAssetBundleRequest(args);
_downloadAssetBundleRequest.SendRequest();
_steps = ESteps.CheckRequest;
}
if (_steps == ESteps.CheckRequest)
{
//TODO 部分小游戏平台的 downloadProgress 始终返回 0导致进度条无法正确显示。
Progress = _downloadAssetBundleRequest.DownloadProgress;
if (_downloadAssetBundleRequest.IsDone == false)
return;
if (_downloadAssetBundleRequest.Status == EDownloadRequestStatus.Succeeded)
{
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadAssetBundleRequest.Url);
var assetBundle = _downloadAssetBundleRequest.Result;
if (assetBundle == null)
{
_steps = ESteps.Done;
SetError($"Downloaded asset bundle is null. URL: {_downloadAssetBundleRequest.Url}");
}
else
{
_steps = ESteps.Done;
SetResult();
BundleHandle = new WebAssetBundleHandle(_options.Bundle, assetBundle, _options.PlatformStrategy);
}
}
else
{
string url = _downloadAssetBundleRequest.Url;
long httpCode = _downloadAssetBundleRequest.HttpCode;
string httpError = _downloadAssetBundleRequest.HttpError;
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
{
_downloadRetryController.StartRetryDelay();
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.Done;
SetError(_downloadAssetBundleRequest.Error);
YooLogger.LogError(Error);
}
}
}
if (_steps == ESteps.TryAgain)
{
// 注意:失败后释放网络请求
if (_downloadAssetBundleRequest != null)
{
_downloadAssetBundleRequest.Dispose();
_downloadAssetBundleRequest = null;
}
if (_downloadRetryController.TickRetryDelay())
{
Progress = 0f;
_steps = ESteps.BundleRequest;
}
}
}
protected override void InternalDispose()
{
if (_downloadAssetBundleRequest != null)
{
_downloadAssetBundleRequest.Dispose();
_downloadAssetBundleRequest = null;
}
}
}
}

View File

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

View File

@@ -0,0 +1,64 @@
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// WebGL 平台加载非加密 AssetBundle 的操作选项
/// </summary>
internal readonly struct LoadWebPlatformAssetBundleOptions
{
/// <summary>
/// 资源包描述
/// </summary>
public PackageBundle Bundle { get; }
/// <summary>
/// 候选下载地址列表
/// </summary>
public IReadOnlyList<string> CandidateUrls { get; }
/// <summary>
/// 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; }
/// <summary>
/// 看门狗超时时间
/// </summary>
public int WatchdogTimeout { get; }
/// <summary>
/// 禁用 Unity 内置网络缓存
/// </summary>
public bool DisableUnityWebCache { get; }
/// <summary>
/// 下载重试判定策略
/// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary>
/// URL 选择策略
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public LoadWebPlatformAssetBundleOptions(PackageBundle bundle, IReadOnlyList<string> candidateUrls,
IWebPlatformStrategy platformStrategy, IDownloadBackend downloadBackend, int watchdogTimeout, bool disableUnityWebCache,
IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
Bundle = bundle;
CandidateUrls = candidateUrls;
PlatformStrategy = platformStrategy;
DownloadBackend = downloadBackend;
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
}
}

View File

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

View File

@@ -38,7 +38,7 @@ namespace YooAsset
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName()); var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
if (_options.Bundle.IsEncrypted) if (_options.Bundle.IsEncrypted)
{ {
var options = new LoadWebAssetBundleOptions( var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
cacheName: _fileCache.GetType().Name, cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle, bundle: _options.Bundle,
candidateUrls: urls, candidateUrls: urls,
@@ -46,10 +46,9 @@ namespace YooAsset
downloadBackend: _fileCache.Config.DownloadBackend, downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel, downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout, watchdogTimeout: _fileCache.Config.WatchdogTimeout,
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy, downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy); downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options); _loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
} }
else else
{ {

View File

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

View File

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

View File

@@ -0,0 +1,17 @@
namespace YooAsset
{
/// <summary>
/// WebGL 平台网络缓存初始化操作
/// </summary>
internal sealed class WNBCInitializeOperation : BCInitializeOperation
{
protected override void InternalStart()
{
SetResult();
}
protected override void InternalUpdate()
{
}
}
}

View File

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

View File

@@ -0,0 +1,103 @@
namespace YooAsset
{
/// <summary>
/// WebGL 平台网络缓存加载 AssetBundle 操作
/// </summary>
internal sealed class WNBCLoadAssetBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
LoadBundle,
Done,
}
private readonly WebNetworkBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal WNBCLoadAssetBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.LoadBundle;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBundle)
{
if (_loadBundleOp == null)
{
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
if (_options.Bundle.IsEncrypted)
{
var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: urls,
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
}
else
{
var platformOptions = new LoadWebPlatformAssetBundleOptions(
bundle: _options.Bundle,
candidateUrls: urls,
platformStrategy: _fileCache.Config.PlatformStrategy,
downloadBackend: _fileCache.Config.DownloadBackend,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebPlatformAssetBundleOperation(platformOptions);
}
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
}
_loadBundleOp.UpdateOperation();
Progress = _loadBundleOp.Progress;
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
throw new YooInternalException("Loaded bundle handle is null.");
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
}
}
}
protected override void InternalWaitForCompletion()
{
if (_steps != ESteps.Done)
{
_steps = ESteps.Done;
SetError("WebGL platform does not support synchronous loading.");
YooLogger.LogError(Error);
}
}
}
}

View File

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

View File

@@ -0,0 +1,86 @@
namespace YooAsset
{
/// <summary>
/// WebGL 平台网络缓存加载 RawBundle 操作
/// </summary>
internal sealed class WNBCLoadRawBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
LoadBundle,
Done,
}
private readonly WebNetworkBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal WNBCLoadRawBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.LoadBundle;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.LoadBundle)
{
if (_loadBundleOp == null)
{
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
var options = new LoadWebRawBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: urls,
rawBundleDecryptor: _fileCache.Config.RawBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebRawBundleOperation(options);
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
}
_loadBundleOp.UpdateOperation();
Progress = _loadBundleOp.Progress;
if (_loadBundleOp.IsDone == false)
return;
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
{
if (_loadBundleOp.BundleHandle == null)
throw new YooInternalException("Loaded bundle handle is null.");
_steps = ESteps.Done;
SetResult();
BundleHandle = _loadBundleOp.BundleHandle;
}
else
{
_steps = ESteps.Done;
SetError(_loadBundleOp.Error);
}
}
}
protected override void InternalWaitForCompletion()
{
if (_steps != ESteps.Done)
{
_steps = ESteps.Done;
SetError("WebGL platform does not support synchronous loading.");
YooLogger.LogError(Error);
}
}
}
}

View File

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

View File

@@ -0,0 +1,167 @@
namespace YooAsset
{
/// <summary>
/// WebGL 平台网络缓存系统
/// </summary>
internal class WebNetworkBundleCache : IBundleCache
{
internal readonly struct Configuration
{
/// <summary>
/// 看门狗超时时间
/// </summary>
public int WatchdogTimeout { get; }
/// <summary>
/// 禁用 Unity 内置网络缓存
/// </summary>
public bool DisableUnityWebCache { get; }
/// <summary>
/// 下载数据校验级别
/// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; }
/// <summary>
/// AssetBundle 解密器
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary>
/// RawBundle 解密器
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary>
/// 平台策略接口
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary>
/// 远程服务接口
/// </summary>
public IRemoteService RemoteService { get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; }
/// <summary>
/// 下载重试判定策略
/// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary>
/// URL 选择策略
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
IWebPlatformStrategy platformStrategy, IRemoteService remoteService, IDownloadBackend downloadBackend,
IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadVerifyLevel = downloadVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
PlatformStrategy = platformStrategy;
RemoteService = remoteService;
DownloadBackend = downloadBackend;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
}
/// <summary>
/// 缓存配置
/// </summary>
internal readonly Configuration Config;
#region
/// <inheritdoc/>
public string PackageName { get; }
/// <inheritdoc/>
public string RootPath { get; }
/// <inheritdoc/>
public bool IsReadOnly { get; }
/// <inheritdoc/>
public int FileCount { get; }
/// <inheritdoc/>
public long SpaceOccupied { get; }
#endregion
public WebNetworkBundleCache(string packageName, Configuration config)
{
PackageName = packageName;
Config = config;
IsReadOnly = true;
}
/// <inheritdoc />
public void Dispose()
{
}
/// <inheritdoc />
public BCInitializeOperation InitializeAsync()
{
var operation = new WNBCInitializeOperation();
return operation;
}
/// <inheritdoc />
public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebNetworkBundleCache)} is readonly.");
return operation;
}
/// <inheritdoc />
public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{
var operation = new BCClearCacheCompleteOperation();
return operation;
}
/// <inheritdoc />
public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{
var operation = new BCVerifyCacheCompleteOperation();
return operation;
}
/// <inheritdoc />
public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{
if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
{
var operation = new WNBCLoadAssetBundleOperation(this, options);
return operation;
}
else if (options.Bundle.GetBundleType() == (int)EBundleType.RawBundle)
{
var operation = new WNBCLoadRawBundleOperation(this, options);
return operation;
}
else
{
string error = $"{nameof(WebNetworkBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error);
return operation;
}
}
/// <inheritdoc />
public bool IsCached(string bundleGuid)
{
return true;
}
/// <inheritdoc />
public string GetCacheFilePath(string bundleGuid)
{
YooLogger.LogWarning($"{nameof(WebNetworkBundleCache)} does not support local cache file path.");
return null;
}
}
}

View File

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

View File

@@ -42,6 +42,22 @@ namespace YooAsset
if (_loadBundleOp == null) if (_loadBundleOp == null)
{ {
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName()); var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
if (_options.Bundle.IsEncrypted)
{
var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: urls,
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
}
else
{
var options = new LoadWebAssetBundleOptions( var options = new LoadWebAssetBundleOptions(
cacheName: _fileCache.GetType().Name, cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle, bundle: _options.Bundle,
@@ -53,11 +69,8 @@ namespace YooAsset
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache, disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy, downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy); downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
if (_options.Bundle.IsEncrypted)
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
else
_loadBundleOp = new LoadWebNormalAssetBundleOperation(options); _loadBundleOp = new LoadWebNormalAssetBundleOperation(options);
}
_loadBundleOp.StartOperation(); _loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp); AddChildOperation(_loadBundleOp);

View File

@@ -58,7 +58,9 @@ namespace YooAsset
if (_loadBundleOp == null) if (_loadBundleOp == null)
{ {
string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath); string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath);
var options = new LoadWebAssetBundleOptions( if (_options.Bundle.IsEncrypted)
{
var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
cacheName: _fileCache.GetType().Name, cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle, bundle: _options.Bundle,
candidateUrls: new[] { url }, candidateUrls: new[] { url },
@@ -66,14 +68,23 @@ namespace YooAsset
downloadBackend: _fileCache.Config.DownloadBackend, downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel, downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout, watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
}
else
{
var platformOptions = new LoadWebPlatformAssetBundleOptions(
bundle: _options.Bundle,
candidateUrls: new[] { url },
platformStrategy: _fileCache.Config.PlatformStrategy,
downloadBackend: _fileCache.Config.DownloadBackend,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache, disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy, downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy); downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebPlatformAssetBundleOperation(platformOptions);
if (_options.Bundle.IsEncrypted) }
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
else
_loadBundleOp = new LoadWebNormalAssetBundleOperation(options);
_loadBundleOp.StartOperation(); _loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp); AddChildOperation(_loadBundleOp);

View File

@@ -35,6 +35,11 @@ namespace YooAsset
/// </summary> /// </summary>
public IBundleDecryptor RawBundleDecryptor { get; } public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary>
/// Web 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary> /// <summary>
/// 下载后台 /// 下载后台
/// </summary> /// </summary>
@@ -52,13 +57,15 @@ namespace YooAsset
public Configuration(int watchdogTimeout, bool disableUnityWebCache, public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy) IWebPlatformStrategy platformStrategy, IDownloadBackend downloadBackend,
IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{ {
WatchdogTimeout = watchdogTimeout; WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache; DisableUnityWebCache = disableUnityWebCache;
DownloadVerifyLevel = downloadVerifyLevel; DownloadVerifyLevel = downloadVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor; AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor; RawBundleDecryptor = rawBundleDecryptor;
PlatformStrategy = platformStrategy;
DownloadBackend = downloadBackend; DownloadBackend = downloadBackend;
DownloadRetryPolicy = downloadRetryPolicy; DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy; DownloadUrlPolicy = downloadUrlPolicy;

View File

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

View File

@@ -0,0 +1,62 @@
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
/// <summary>
/// WebGL 平台 AssetBundle 资源包句柄
/// </summary>
internal sealed class WebAssetBundleHandle : IBundleHandle
{
private readonly PackageBundle _packageBundle;
private readonly AssetBundle _assetBundle;
private readonly IWebPlatformStrategy _platformStrategy;
public WebAssetBundleHandle(PackageBundle packageBundle, AssetBundle assetBundle, IWebPlatformStrategy platformStrategy)
{
_packageBundle = packageBundle;
_assetBundle = assetBundle;
_platformStrategy = platformStrategy;
}
/// <inheritdoc/>
public void UnloadBundle()
{
if (_assetBundle != null)
{
if (_packageBundle.IsEncrypted)
_assetBundle.Unload(true);
else
_platformStrategy.UnloadAssetBundle(_assetBundle, true);
}
}
/// <inheritdoc/>
public BHLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
{
var operation = new ABHLoadAssetOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
/// <inheritdoc/>
public BHLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
{
var operation = new ABHLoadAllAssetsOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
/// <inheritdoc/>
public BHLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
{
var operation = new ABHLoadSubAssetsOperation(_packageBundle, _assetBundle, assetInfo);
return operation;
}
/// <inheritdoc/>
public BHLoadSceneOperation LoadSceneAsync(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation)
{
var operation = new ABHLoadSceneOperation(assetInfo, loadSceneParams, allowSceneActivation);
return operation;
}
}
}

View File

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

View File

@@ -107,9 +107,9 @@ namespace YooAsset
int timeout, int timeout,
int watchdogTimeout, int watchdogTimeout,
string savePath, string savePath,
bool appendToFile = false, bool appendToFile,
bool removeFileOnAbort = true, bool removeFileOnAbort,
long resumeOffset = 0, long resumeOffset,
IReadOnlyDictionary<string, string> headers = null) IReadOnlyDictionary<string, string> headers = null)
{ {
RequestArgs = new DownloadRequestArgs( RequestArgs = new DownloadRequestArgs(
@@ -122,6 +122,32 @@ namespace YooAsset
RemoveFileOnAbort = removeFileOnAbort; RemoveFileOnAbort = removeFileOnAbort;
ResumeOffset = resumeOffset; ResumeOffset = resumeOffset;
} }
/// <summary>
/// 构造文件下载请求参数
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="timeout">响应超时时间0 表示不应用超时。</param>
/// <param name="watchdogTimeout">看门狗超时时间0 表示禁用。</param>
/// <param name="savePath">文件保存路径</param>
/// <param name="headers">自定义请求头(可选)</param>
public DownloadFileRequestArgs(
string url,
int timeout,
int watchdogTimeout,
string savePath,
IReadOnlyDictionary<string, string> headers = null)
{
RequestArgs = new DownloadRequestArgs(
url: url,
timeout: timeout,
watchdogTimeout: watchdogTimeout,
headers: headers);
SavePath = savePath;
AppendToFile = false;
RemoveFileOnAbort = true;
ResumeOffset = 0;
}
} }
/// <summary> /// <summary>
@@ -185,6 +211,11 @@ namespace YooAsset
/// </summary> /// </summary>
public uint UnityCrc { get; } public uint UnityCrc { get; }
/// <summary>
/// Web 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary> /// <summary>
/// 构造 AssetBundle 下载请求参数 /// 构造 AssetBundle 下载请求参数
/// </summary> /// </summary>
@@ -194,14 +225,16 @@ namespace YooAsset
/// <param name="disableUnityWebCache">是否禁用 Unity 内置缓存</param> /// <param name="disableUnityWebCache">是否禁用 Unity 内置缓存</param>
/// <param name="fileHash">文件哈希(启用缓存时必须提供)</param> /// <param name="fileHash">文件哈希(启用缓存时必须提供)</param>
/// <param name="unityCrc">Unity CRC 校验值</param> /// <param name="unityCrc">Unity CRC 校验值</param>
/// <param name="platformStrategy">WebGL 平台策略</param>
/// <param name="headers">自定义请求头(可选)</param> /// <param name="headers">自定义请求头(可选)</param>
public DownloadAssetBundleRequestArgs( public DownloadAssetBundleRequestArgs(
string url, string url,
int timeout, int timeout,
int watchdogTimeout, int watchdogTimeout,
bool disableUnityWebCache = true, bool disableUnityWebCache,
string fileHash = null, string fileHash,
uint unityCrc = 0, uint unityCrc,
IWebPlatformStrategy platformStrategy,
IReadOnlyDictionary<string, string> headers = null) IReadOnlyDictionary<string, string> headers = null)
{ {
RequestArgs = new DownloadRequestArgs( RequestArgs = new DownloadRequestArgs(
@@ -212,6 +245,7 @@ namespace YooAsset
DisableUnityWebCache = disableUnityWebCache; DisableUnityWebCache = disableUnityWebCache;
FileHash = fileHash; FileHash = fileHash;
UnityCrc = unityCrc; UnityCrc = unityCrc;
PlatformStrategy = platformStrategy;
} }
} }
@@ -243,12 +277,12 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="url">请求地址(仅用于标识)</param> /// <param name="url">请求地址(仅用于标识)</param>
/// <param name="fileSize">模拟的文件大小(字节)</param> /// <param name="fileSize">模拟的文件大小(字节)</param>
/// <param name="downloadSpeed">模拟的下载速度(字节/秒),默认 1MB/s</param> /// <param name="downloadSpeed">模拟的下载速度(字节/秒)</param>
public SimulatedDownloadRequestArgs(string url, long fileSize, long downloadSpeed = 1024 * 1024) public SimulatedDownloadRequestArgs(string url, long fileSize, long downloadSpeed)
{ {
Url = url; Url = url;
FileSize = Math.Max(fileSize, 0); FileSize = Math.Max(fileSize, 0);
DownloadSpeed = downloadSpeed > 0 ? downloadSpeed : 1024 * 1024; DownloadSpeed = downloadSpeed;
} }
} }
} }

View File

@@ -1,4 +1,3 @@
using System;
using UnityEngine; using UnityEngine;
using UnityEngine.Networking; using UnityEngine.Networking;
@@ -7,21 +6,10 @@ namespace YooAsset
/// <summary> /// <summary>
/// UnityWebRequest AssetBundle 下载器 /// UnityWebRequest AssetBundle 下载器
/// </summary> /// </summary>
/// <remarks>
/// <para>下载并加载 Unity AssetBundle 资源包</para>
/// <para>支持 Unity 内置缓存机制和 CRC 校验</para>
/// </remarks>
internal sealed class UnityWebRequestAssetBundle : UnityWebRequestBase, IDownloadAssetBundleRequest internal sealed class UnityWebRequestAssetBundle : UnityWebRequestBase, IDownloadAssetBundleRequest
{ {
/// <summary>
/// AssetBundle 下载参数
/// </summary>
private readonly DownloadAssetBundleRequestArgs _args; private readonly DownloadAssetBundleRequestArgs _args;
private readonly IWebPlatformStrategy _platformStrategy;
/// <summary>
/// AssetBundle 下载处理器
/// </summary>
private DownloadHandlerAssetBundle _downloadHandler;
/// <summary> /// <summary>
/// 下载结果AssetBundle 对象) /// 下载结果AssetBundle 对象)
@@ -37,43 +25,22 @@ namespace YooAsset
: base(args.RequestArgs, webRequestCreator) : base(args.RequestArgs, webRequestCreator)
{ {
_args = args; _args = args;
_platformStrategy = args.PlatformStrategy;
} }
protected override UnityWebRequest CreateWebRequest() protected override UnityWebRequest CreateWebRequest()
{ {
_downloadHandler = CreateAssetBundleDownloadHandler(); var args = new WebAssetBundleRequestArgs(
var request = CreateGetWebRequest(Url); url: _args.RequestArgs.Url,
request.downloadHandler = _downloadHandler; disableUnityWebCache: _args.DisableUnityWebCache,
request.disposeDownloadHandlerOnDispose = true; fileHash: _args.FileHash,
return request; unityCrc: _args.UnityCrc);
return _platformStrategy.CreateAssetBundleRequest(args);
} }
protected override void OnRequestSucceeded(UnityWebRequest webRequest) protected override void OnRequestSucceeded(UnityWebRequest webRequest)
{ {
Result = _downloadHandler.assetBundle; Result = _platformStrategy.ExtractAssetBundle(webRequest);
}
private DownloadHandlerAssetBundle CreateAssetBundleDownloadHandler()
{
DownloadHandlerAssetBundle handler;
if (_args.DisableUnityWebCache)
{
// 禁用 Unity 缓存
handler = new DownloadHandlerAssetBundle(Url, _args.UnityCrc);
}
else
{
if (string.IsNullOrEmpty(_args.FileHash))
throw new YooInternalException("FileHash is required when Unity web cache is enabled (DisableUnityWebCache = false).");
// 使用 Unity 缓存
// 说明The file hash defining the version of the asset bundle.
Hash128 fileHash = Hash128.Parse(_args.FileHash);
handler = new DownloadHandlerAssetBundle(Url, fileHash, _args.UnityCrc);
}
return handler;
} }
} }
} }

View File

@@ -135,5 +135,10 @@ namespace YooAsset
/// URL 选择策略 <see cref="IDownloadUrlPolicy"/> /// URL 选择策略 <see cref="IDownloadUrlPolicy"/>
/// </summary> /// </summary>
DownloadUrlPolicy, DownloadUrlPolicy,
/// <summary>
/// WebGL 平台策略 <see cref="IWebPlatformStrategy"/>
/// </summary>
WebPlatformStrategy,
} }
} }

View File

@@ -102,20 +102,31 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="packageRoot">文件系统的根目录</param> /// <param name="packageRoot">文件系统的根目录</param>
/// <returns>配置好的文件系统参数实例</returns> /// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultBuiltinFileSystemParameters(string packageRoot = null) public static FileSystemParameters CreateDefaultBuiltinFileSystemParameters(string packageRoot)
{ {
string fileSystemClass = typeof(BuiltinFileSystem).FullName; string fileSystemClass = typeof(BuiltinFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot); var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
return fileSystemParams; return fileSystemParams;
} }
/// <summary>
/// 创建默认的内置文件系统参数
/// </summary>
/// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultBuiltinFileSystemParameters()
{
string fileSystemClass = typeof(BuiltinFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
return fileSystemParams;
}
/// <summary> /// <summary>
/// 创建默认的沙盒文件系统参数 /// 创建默认的沙盒文件系统参数
/// </summary> /// </summary>
/// <param name="remoteService">远端资源地址查询服务类</param> /// <param name="remoteService">远端资源地址查询服务类</param>
/// <param name="packageRoot">文件系统的根目录</param> /// <param name="packageRoot">文件系统的根目录</param>
/// <returns>配置好的文件系统参数实例</returns> /// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultSandboxFileSystemParameters(IRemoteService remoteService, string packageRoot = null) public static FileSystemParameters CreateDefaultSandboxFileSystemParameters(IRemoteService remoteService, string packageRoot)
{ {
string fileSystemClass = typeof(SandboxFileSystem).FullName; string fileSystemClass = typeof(SandboxFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot); var fileSystemParams = new FileSystemParameters(fileSystemClass, packageRoot);
@@ -124,11 +135,24 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 创建默认的WebServer文件系统参数 /// 创建默认的沙盒文件系统参数
/// </summary>
/// <param name="remoteService">远端资源地址查询服务类</param>
/// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultSandboxFileSystemParameters(IRemoteService remoteService)
{
string fileSystemClass = typeof(SandboxFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter(EFileSystemParameter.RemoteService, remoteService);
return fileSystemParams;
}
/// <summary>
/// 创建默认的WebGL 服务器文件系统参数
/// </summary> /// </summary>
/// <param name="disableUnityWebCache">禁用Unity的网络缓存</param> /// <param name="disableUnityWebCache">禁用Unity的网络缓存</param>
/// <returns>配置好的文件系统参数实例</returns> /// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultWebServerFileSystemParameters(bool disableUnityWebCache = false) public static FileSystemParameters CreateDefaultWebServerFileSystemParameters(bool disableUnityWebCache)
{ {
string fileSystemClass = typeof(WebServerFileSystem).FullName; string fileSystemClass = typeof(WebServerFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null); var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
@@ -137,19 +161,45 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 创建默认的WebRemote文件系统参数 /// 创建默认的WebGL 服务器文件系统参数
/// </summary>
/// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultWebServerFileSystemParameters()
{
string fileSystemClass = typeof(WebServerFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter(EFileSystemParameter.DisableUnityWebCache, false);
return fileSystemParams;
}
/// <summary>
/// 创建默认的 WebGL 网络文件系统参数
/// </summary> /// </summary>
/// <param name="remoteService">远端资源地址查询服务类</param> /// <param name="remoteService">远端资源地址查询服务类</param>
/// <param name="disableUnityWebCache">禁用Unity的网络缓存</param> /// <param name="disableUnityWebCache">禁用Unity的网络缓存</param>
/// <returns>配置好的文件系统参数实例</returns> /// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultWebRemoteFileSystemParameters(IRemoteService remoteService, bool disableUnityWebCache = false) public static FileSystemParameters CreateDefaultWebNetworkFileSystemParameters(IRemoteService remoteService, bool disableUnityWebCache)
{ {
string fileSystemClass = typeof(WebRemoteFileSystem).FullName; string fileSystemClass = typeof(WebNetworkFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null); var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter(EFileSystemParameter.RemoteService, remoteService); fileSystemParams.AddParameter(EFileSystemParameter.RemoteService, remoteService);
fileSystemParams.AddParameter(EFileSystemParameter.DisableUnityWebCache, disableUnityWebCache); fileSystemParams.AddParameter(EFileSystemParameter.DisableUnityWebCache, disableUnityWebCache);
return fileSystemParams; return fileSystemParams;
} }
/// <summary>
/// 创建默认的 WebGL 网络文件系统参数
/// </summary>
/// <param name="remoteService">远端资源地址查询服务类</param>
/// <returns>配置好的文件系统参数实例</returns>
public static FileSystemParameters CreateDefaultWebNetworkFileSystemParameters(IRemoteService remoteService)
{
string fileSystemClass = typeof(WebNetworkFileSystem).FullName;
var fileSystemParams = new FileSystemParameters(fileSystemClass, null);
fileSystemParams.AddParameter(EFileSystemParameter.RemoteService, remoteService);
fileSystemParams.AddParameter(EFileSystemParameter.DisableUnityWebCache, false);
return fileSystemParams;
}
#endregion #endregion
} }
} }

View File

@@ -0,0 +1,73 @@
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// Web 平台 AssetBundle 请求创建参数
/// </summary>
internal readonly struct WebAssetBundleRequestArgs
{
/// <summary>
/// 请求地址
/// </summary>
public string Url { get; }
/// <summary>
/// 禁用 Unity 的网络缓存
/// </summary>
public bool DisableUnityWebCache { get; }
/// <summary>
/// AssetBundle 文件哈希(用于 UnityWebRequest 的缓存)
/// </summary>
public string FileHash { get; }
/// <summary>
/// Unity CRC 校验值
/// </summary>
public uint UnityCrc { get; }
/// <summary>
/// 创建 <see cref="WebAssetBundleRequestArgs"/> 实例
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="disableUnityWebCache">是否禁用 Unity 内置缓存</param>
/// <param name="fileHash">文件哈希(启用缓存时必须提供)</param>
/// <param name="unityCrc">Unity CRC 校验值</param>
internal WebAssetBundleRequestArgs(string url, bool disableUnityWebCache, string fileHash, uint unityCrc)
{
Url = url;
DisableUnityWebCache = disableUnityWebCache;
FileHash = fileHash;
UnityCrc = unityCrc;
}
}
/// <summary>
/// Web 平台策略接口
/// </summary>
internal interface IWebPlatformStrategy
{
/// <summary>
/// 创建平台专用的 AssetBundle 下载请求
/// </summary>
/// <param name="args">AssetBundle 下载参数</param>
/// <returns>已配置的 UnityWebRequest 实例</returns>
UnityWebRequest CreateAssetBundleRequest(WebAssetBundleRequestArgs args);
/// <summary>
/// 从已完成的请求中提取 AssetBundle 对象
/// </summary>
/// <param name="request">已完成下载的 UnityWebRequest</param>
/// <returns>提取到的 AssetBundle若提取失败则返回 null。</returns>
AssetBundle ExtractAssetBundle(UnityWebRequest request);
/// <summary>
/// 使用平台专用 API 卸载 AssetBundle
/// </summary>
/// <param name="assetBundle">待卸载的 AssetBundle 实例</param>
/// <param name="unloadAll">是否同时卸载所有已加载的资源对象</param>
void UnloadAssetBundle(AssetBundle assetBundle, bool unloadAll);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,107 @@
namespace YooAsset
{
/// <summary>
/// Web 网络文件系统的加载包裹清单操作
/// </summary>
internal sealed class WNFSLoadPackageManifestOperation : FSLoadPackageManifestOperation
{
private enum ESteps
{
None,
RequestPackageHash,
LoadPackageManifest,
Done,
}
private readonly WebNetworkFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private RequestWebPackageHashOperation _requestWebPackageHashOp;
private LoadWebPackageManifestOperation _loadWebPackageManifestOp;
private ESteps _steps = ESteps.None;
public WNFSLoadPackageManifestOperation(WebNetworkFileSystem fileSystem, string packageVersion, int timeout)
{
_fileSystem = fileSystem;
_packageVersion = packageVersion;
_timeout = timeout;
}
protected override void InternalStart()
{
_steps = ESteps.RequestPackageHash;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageHash)
{
if (_requestWebPackageHashOp == null)
{
var options = new RequestWebPackageHashOptions(
packageName: _fileSystem.PackageName,
packageVersion: _packageVersion,
timeout: _timeout,
remoteService: _fileSystem.RemoteService,
downloadBackend: _fileSystem.DownloadBackend,
downloadUrlPolicy: _fileSystem.DownloadUrlPolicy);
_requestWebPackageHashOp = new RequestWebPackageHashOperation(options);
_requestWebPackageHashOp.StartOperation();
AddChildOperation(_requestWebPackageHashOp);
}
_requestWebPackageHashOp.UpdateOperation();
if (_requestWebPackageHashOp.IsDone == false)
return;
if (_requestWebPackageHashOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.LoadPackageManifest;
}
else
{
_steps = ESteps.Done;
SetError(_requestWebPackageHashOp.Error);
}
}
if (_steps == ESteps.LoadPackageManifest)
{
if (_loadWebPackageManifestOp == null)
{
var options = new LoadWebPackageManifestOptions(
packageName: _fileSystem.PackageName,
packageVersion: _packageVersion,
packageHash: _requestWebPackageHashOp.PackageHash,
timeout: _timeout,
remoteService: _fileSystem.RemoteService,
manifestDecryptor: _fileSystem.ManifestDecryptor,
downloadBackend: _fileSystem.DownloadBackend,
downloadUrlPolicy: _fileSystem.DownloadUrlPolicy);
_loadWebPackageManifestOp = new LoadWebPackageManifestOperation(options);
_loadWebPackageManifestOp.StartOperation();
AddChildOperation(_loadWebPackageManifestOp);
}
_loadWebPackageManifestOp.UpdateOperation();
Progress = _loadWebPackageManifestOp.Progress;
if (_loadWebPackageManifestOp.IsDone == false)
return;
if (_loadWebPackageManifestOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
Manifest = _loadWebPackageManifestOp.Manifest;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_loadWebPackageManifestOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,72 @@
namespace YooAsset
{
/// <summary>
/// Web 网络文件系统的查询包裹版本操作
/// </summary>
internal sealed class WNFSRequestPackageVersionOperation : FSRequestPackageVersionOperation
{
private enum ESteps
{
None,
RequestPackageVersion,
Done,
}
private readonly WebNetworkFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private RequestWebPackageVersionOperation _requestWebPackageVersionOp;
private ESteps _steps = ESteps.None;
internal WNFSRequestPackageVersionOperation(WebNetworkFileSystem fileSystem, bool appendTimeTicks, int timeout)
{
_fileSystem = fileSystem;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
protected override void InternalStart()
{
_steps = ESteps.RequestPackageVersion;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.RequestPackageVersion)
{
if (_requestWebPackageVersionOp == null)
{
var options = new RequestWebPackageVersionOptions(
packageName: _fileSystem.PackageName,
appendTimeTicks: _appendTimeTicks,
timeout: _timeout,
remoteService: _fileSystem.RemoteService,
downloadBackend: _fileSystem.DownloadBackend,
downloadUrlPolicy: _fileSystem.DownloadUrlPolicy);
_requestWebPackageVersionOp = new RequestWebPackageVersionOperation(options);
_requestWebPackageVersionOp.StartOperation();
AddChildOperation(_requestWebPackageVersionOp);
}
_requestWebPackageVersionOp.UpdateOperation();
Progress = _requestWebPackageVersionOp.Progress;
if (_requestWebPackageVersionOp.IsDone == false)
return;
if (_requestWebPackageVersionOp.Status == EOperationStatus.Succeeded)
{
_steps = ESteps.Done;
PackageVersion = _requestWebPackageVersionOp.PackageVersion;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError(_requestWebPackageVersionOp.Error);
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,261 @@
using System;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// Web 网络文件系统,统一管理普通 Web 和 Mini Game 平台的远程资源加载。
/// 通过 IWebPlatformStrategy 隔离不同平台的 AssetBundle 请求、提取和卸载行为。
/// </summary>
internal class WebNetworkFileSystem : IFileSystem
{
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { get; private set; }
/// <summary>
/// Web 文件缓存系统
/// </summary>
public IBundleCache BundleCache { get; private set; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; private set; }
/// <summary>
/// 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; private set; }
#region
/// <summary>
/// 自定义参数UnityWebRequest 创建委托
/// </summary>
public UnityWebRequestCreator WebRequestCreator { get; private set; }
/// <summary>
/// 自定义参数:禁用 Unity 内置网络缓存
/// </summary>
public bool DisableUnityWebCache { get; private set; } = false;
/// <summary>
/// 自定义参数:下载任务的看门狗机制超时时间
/// </summary>
public int DownloadWatchdogTimeout { get; private set; } = 0;
/// <summary>
/// 自定义参数:下载的资源包数据的校验级别
/// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; private set; } = EFileVerifyLevel.Middle;
/// <summary>
/// 远程服务接口
/// </summary>
public IRemoteService RemoteService { get; private set; }
/// <summary>
/// 自定义参数AssetBundle 解密器
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数RawBundle 解密器
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; private set; }
/// <summary>
/// 自定义参数:资源清单解密器
/// </summary>
public IManifestDecryptor ManifestDecryptor { get; private set; }
/// <summary>
/// 自定义参数:下载重试判定策略
/// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; private set; }
/// <summary>
/// 自定义参数URL 选择策略
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; private set; }
#endregion
public WebNetworkFileSystem()
{
}
/// <inheritdoc />
public virtual FSInitializeOperation InitializeAsync()
{
var operation = new WNFSInitializeOperation(this);
return operation;
}
/// <inheritdoc />
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(FSRequestPackageVersionOptions options)
{
var operation = new WNFSRequestPackageVersionOperation(this, options.AppendTimeTicks, options.Timeout);
return operation;
}
/// <inheritdoc />
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(FSLoadPackageManifestOptions options)
{
var operation = new WNFSLoadPackageManifestOperation(this, options.PackageVersion, options.Timeout);
return operation;
}
/// <inheritdoc />
public virtual FSLoadPackageBundleOperation LoadPackageBundleAsync(FSLoadPackageBundleOptions options)
{
var operation = new WNFSLoadPackageBundleOperation(this, options);
return operation;
}
/// <inheritdoc />
public virtual FSEnsurePackageBundleOperation EnsurePackageBundleAsync(FSEnsurePackageBundleOptions options)
{
var operation = new FSEnsurePackageBundleFailureOperation($"{nameof(WebNetworkFileSystem)} does not support ensure bundle file operation.");
return operation;
}
/// <inheritdoc />
public virtual FSDownloadBundleOperation DownloadBundleAsync(FSDownloadBundleOptions options)
{
var operation = new FSDownloadBundleCompleteOperation($"{nameof(WebNetworkFileSystem)} does not support download operation.");
return operation;
}
/// <inheritdoc />
public virtual FSClearCacheOperation ClearCacheAsync(FSClearCacheOptions options)
{
var operation = new FSClearCacheCompleteOperation();
return operation;
}
/// <inheritdoc />
public virtual void SetParameter(string paramName, object value)
{
if (paramName == nameof(EFileSystemParameter.DownloadBackend))
{
DownloadBackend = FileSystemHelper.CastParameter<IDownloadBackend>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.UnityWebRequestCreator))
{
WebRequestCreator = FileSystemHelper.CastParameter<UnityWebRequestCreator>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DisableUnityWebCache))
{
DisableUnityWebCache = FileSystemHelper.CastParameter<bool>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadWatchdogTimeout))
{
int convertValue = FileSystemHelper.CastParameter<int>(paramName, value);
DownloadWatchdogTimeout = Mathf.Max(convertValue, 0);
}
else if (paramName == nameof(EFileSystemParameter.FileVerifyLevel))
{
DownloadVerifyLevel = FileSystemHelper.CastParameter<EFileVerifyLevel>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RemoteService))
{
RemoteService = FileSystemHelper.CastParameter<IRemoteService>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.AssetbundleDecryptor))
{
AssetBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.RawbundleDecryptor))
{
RawBundleDecryptor = FileSystemHelper.CastParameter<IBundleDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.ManifestDecryptor))
{
ManifestDecryptor = FileSystemHelper.CastParameter<IManifestDecryptor>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadRetryPolicy))
{
DownloadRetryPolicy = FileSystemHelper.CastParameter<IDownloadRetryPolicy>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.DownloadUrlPolicy))
{
DownloadUrlPolicy = FileSystemHelper.CastParameter<IDownloadUrlPolicy>(paramName, value);
}
else if (paramName == nameof(EFileSystemParameter.WebPlatformStrategy))
{
PlatformStrategy = FileSystemHelper.CastParameter<IWebPlatformStrategy>(paramName, value);
}
else
{
throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName));
}
}
/// <inheritdoc />
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(WebRequestCreator);
// 创建默认的下载重试策略
if (DownloadRetryPolicy == null)
DownloadRetryPolicy = new DefaultDownloadRetryPolicy();
// 创建默认的 URL 选择策略
if (DownloadUrlPolicy == null)
DownloadUrlPolicy = new DefaultDownloadUrlPolicy();
// 创建默认的平台策略
if (PlatformStrategy == null)
PlatformStrategy = new DefaultWebPlatformStrategy(WebRequestCreator);
// 创建文件缓存系统
var cacheConfig = new WebNetworkBundleCache.Configuration(
watchdogTimeout: DownloadWatchdogTimeout,
disableUnityWebCache: DisableUnityWebCache,
downloadVerifyLevel: DownloadVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor,
platformStrategy: PlatformStrategy,
remoteService: RemoteService,
downloadBackend: DownloadBackend,
downloadRetryPolicy: DownloadRetryPolicy,
downloadUrlPolicy: DownloadUrlPolicy);
BundleCache = new WebNetworkBundleCache(packageName, cacheConfig);
}
/// <inheritdoc />
public virtual void OnDestroy()
{
if (BundleCache != null)
{
BundleCache.Dispose();
BundleCache = null;
}
if (DownloadBackend != null)
{
DownloadBackend.Dispose();
DownloadBackend = null;
}
}
/// <inheritdoc />
public virtual bool CanAcceptBundle(PackageBundle bundle)
{
// 注意:保底加载!
return true;
}
/// <inheritdoc />
public virtual bool IsDownloadRequired(PackageBundle bundle)
{
return false;
}
/// <inheritdoc />
public virtual bool IsUnpackRequired(PackageBundle bundle)
{
return false;
}
/// <inheritdoc />
public virtual bool IsImportRequired(PackageBundle bundle)
{
return false;
}
}
}

View File

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

View File

@@ -30,6 +30,11 @@ namespace YooAsset
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; private set; } public IDownloadBackend DownloadBackend { get; private set; }
/// <summary>
/// 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; private set; }
/// <summary> /// <summary>
/// 包裹名称 /// 包裹名称
/// </summary> /// </summary>
@@ -175,6 +180,10 @@ namespace YooAsset
{ {
DownloadUrlPolicy = FileSystemHelper.CastParameter<IDownloadUrlPolicy>(paramName, value); DownloadUrlPolicy = FileSystemHelper.CastParameter<IDownloadUrlPolicy>(paramName, value);
} }
else if (paramName == nameof(EFileSystemParameter.WebPlatformStrategy))
{
PlatformStrategy = FileSystemHelper.CastParameter<IWebPlatformStrategy>(paramName, value);
}
else else
{ {
throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName)); throw new ArgumentException($"Unrecognized parameter name: '{paramName}'.", nameof(paramName));
@@ -202,6 +211,10 @@ namespace YooAsset
if (DownloadUrlPolicy == null) if (DownloadUrlPolicy == null)
DownloadUrlPolicy = new DefaultDownloadUrlPolicy(); DownloadUrlPolicy = new DefaultDownloadUrlPolicy();
// 创建默认的平台策略
if (PlatformStrategy == null)
PlatformStrategy = new DefaultWebPlatformStrategy(WebRequestCreator);
// 创建Web文件缓存系统 // 创建Web文件缓存系统
var cacheConfig = new WebServerBundleCache.Configuration( var cacheConfig = new WebServerBundleCache.Configuration(
watchdogTimeout: DownloadWatchdogTimeout, watchdogTimeout: DownloadWatchdogTimeout,
@@ -209,6 +222,7 @@ namespace YooAsset
downloadVerifyLevel: DownloadVerifyLevel, downloadVerifyLevel: DownloadVerifyLevel,
assetBundleDecryptor: AssetBundleDecryptor, assetBundleDecryptor: AssetBundleDecryptor,
rawBundleDecryptor: RawBundleDecryptor, rawBundleDecryptor: RawBundleDecryptor,
platformStrategy: PlatformStrategy,
downloadBackend: DownloadBackend, downloadBackend: DownloadBackend,
downloadRetryPolicy: DownloadRetryPolicy, downloadRetryPolicy: DownloadRetryPolicy,
downloadUrlPolicy: DownloadUrlPolicy); downloadUrlPolicy: DownloadUrlPolicy);

View File

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

View File

@@ -0,0 +1,67 @@
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 网页端平台策略的默认实现
/// </summary>
internal sealed class DefaultWebPlatformStrategy : IWebPlatformStrategy
{
private readonly UnityWebRequestCreator _webRequestCreator;
public DefaultWebPlatformStrategy(UnityWebRequestCreator webRequestCreator)
{
_webRequestCreator = webRequestCreator;
}
/// <inheritdoc/>
public UnityWebRequest CreateAssetBundleRequest(WebAssetBundleRequestArgs args)
{
var downloadHandler = CreateAssetBundleDownloadHandler(args);
var request = CreateGetWebRequest(args.Url);
request.downloadHandler = downloadHandler;
request.disposeDownloadHandlerOnDispose = true;
return request;
}
/// <inheritdoc/>
public AssetBundle ExtractAssetBundle(UnityWebRequest request)
{
var handler = (DownloadHandlerAssetBundle)request.downloadHandler;
return handler.assetBundle;
}
/// <inheritdoc/>
public void UnloadAssetBundle(AssetBundle assetBundle, bool unloadAll)
{
assetBundle.Unload(unloadAll);
}
private UnityWebRequest CreateGetWebRequest(string url)
{
if (_webRequestCreator != null)
return _webRequestCreator.Invoke(url, UnityWebRequest.kHttpVerbGET);
return new UnityWebRequest(url, UnityWebRequest.kHttpVerbGET);
}
private DownloadHandlerAssetBundle CreateAssetBundleDownloadHandler(WebAssetBundleRequestArgs args)
{
if (args.DisableUnityWebCache)
{
// 禁用 Unity 缓存
return new DownloadHandlerAssetBundle(args.Url, args.UnityCrc);
}
else
{
if (string.IsNullOrEmpty(args.FileHash))
throw new YooInternalException("FileHash is required when Unity web cache is enabled (DisableUnityWebCache = false).");
// 使用 Unity 缓存
// 说明The file hash defining the version of the asset bundle.
Hash128 fileHash = Hash128.Parse(args.FileHash);
return new DownloadHandlerAssetBundle(args.Url, fileHash, args.UnityCrc);
}
}
}
}

View File

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

View File

@@ -106,7 +106,7 @@ namespace YooAsset
else if (_playMode == EPlayMode.WebPlayMode) else if (_playMode == EPlayMode.WebPlayMode)
{ {
var initializeParameters = _options as WebPlayModeOptions; var initializeParameters = _options as WebPlayModeOptions;
_initializeFileSystemOp = _fileSystemHost.InitializeAsync(initializeParameters.WebServerFileSystemParameters, initializeParameters.WebRemoteFileSystemParameters); _initializeFileSystemOp = _fileSystemHost.InitializeAsync(initializeParameters.WebServerFileSystemParameters, initializeParameters.WebNetworkFileSystemParameters);
} }
else if (_playMode == EPlayMode.CustomPlayMode) else if (_playMode == EPlayMode.CustomPlayMode)
{ {

View File

@@ -72,9 +72,9 @@ namespace YooAsset
public FileSystemParameters WebServerFileSystemParameters { get; set; } public FileSystemParameters WebServerFileSystemParameters { get; set; }
/// <summary> /// <summary>
/// Web远程文件系统初始化参数 /// Web 网络文件系统初始化参数
/// </summary> /// </summary>
public FileSystemParameters WebRemoteFileSystemParameters { get; set; } public FileSystemParameters WebNetworkFileSystemParameters { get; set; }
} }
/// <summary> /// <summary>