refactor : 重构网络下载模块

新增通用下载接口,扩展了默认的Unity引擎下载器
This commit is contained in:
何冠峰
2026-01-05 19:44:10 +08:00
parent 1884fab0c2
commit c87efdb509
89 changed files with 2597 additions and 1641 deletions

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a8f060786f8775b4a82cc3f55d9135e2
guid: bb70e2274ce5a5d419cfbd7212efdf4a
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,110 @@
using System;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest 下载后台
/// </summary>
/// <remarks>
/// 基于 Unity 内置 UnityWebRequest 实现的下载后台。
/// 支持自定义 UnityWebRequest 创建方式,例如添加证书验证、代理设置等。
/// </remarks>
internal sealed class UnityWebRequestBackend : IDownloadBackend
{
private readonly UnityWebRequestDelegate _webRequestCreator;
/// <summary>
/// 后端名称
/// </summary>
public string Name
{
get { return nameof(UnityWebRequestBackend); }
}
/// <summary>
/// 创建 UnityWebRequest 下载后端(使用默认创建方式)
/// </summary>
public UnityWebRequestBackend() : this(null)
{
}
/// <summary>
/// 创建 UnityWebRequest 下载后端
/// </summary>
/// <param name="webRequestCreator">
/// 自定义 UnityWebRequest 创建委托(可选)。
/// 如果为 null则使用默认的 UnityWebRequest 构造方式。
/// </param>
public UnityWebRequestBackend(UnityWebRequestDelegate webRequestCreator)
{
_webRequestCreator = webRequestCreator;
}
/// <summary>
/// 驱动更新
/// </summary>
/// <remarks>
/// UnityWebRequest 由 Unity 引擎自动驱动,无需额外更新。
/// </remarks>
public void Update()
{
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
// 无需释放资源
}
/// <summary>
/// 创建 HEAD 请求
/// </summary>
public IDownloadHeadRequest CreateHeadRequest(DownloadDataRequestArgs args)
{
return new UnityWebRequestHeadDownloader(args, _webRequestCreator);
}
/// <summary>
/// 创建文件下载请求
/// </summary>
public IDownloadFileRequest CreateFileRequest(DownloadFileRequestArgs args)
{
return new UnityWebRequestFileDownloader(args, _webRequestCreator);
}
/// <summary>
/// 创建字节下载请求
/// </summary>
public IDownloadBytesRequest CreateBytesRequest(DownloadDataRequestArgs args)
{
return new UnityWebRequestBytesDownloader(args, _webRequestCreator);
}
/// <summary>
/// 创建文本下载请求
/// </summary>
public IDownloadTextRequest CreateTextRequest(DownloadDataRequestArgs args)
{
return new UnityWebRequestTextDownloader(args, _webRequestCreator);
}
/// <summary>
/// 创建 AssetBundle 下载请求
/// </summary>
public IDownloadAssetBundleRequest CreateAssetBundleRequest(DownloadAssetBundleRequestArgs args)
{
return new UnityWebRequestAssetBundleDownloader(args, _webRequestCreator);
}
/// <summary>
/// 创建模拟下载请求
/// </summary>
public IDownloadFileRequest CreateSimulateRequest(DownloadSimulateRequestArgs args)
{
return new VirtualFileDownloader(args);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,92 @@
using System;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest AssetBundle 下载器
/// </summary>
/// <remarks>
/// 下载并加载 Unity AssetBundle 资源包。
/// 支持 Unity 内置缓存机制和 CRC 校验。
/// </remarks>
internal sealed class UnityWebRequestAssetBundleDownloader : UnityWebRequestDownloaderBase, IDownloadAssetBundleRequest
{
private readonly DownloadAssetBundleRequestArgs _args;
private DownloadHandlerAssetBundle _downloadHandler;
/// <summary>
/// 下载结果AssetBundle 对象)
/// </summary>
public AssetBundle Result { get; private set; }
/// <summary>
/// 构造 AssetBundle 下载器
/// </summary>
/// <param name="args">AssetBundle 下载参数</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
public UnityWebRequestAssetBundleDownloader(DownloadAssetBundleRequestArgs args, UnityWebRequestDelegate webRequestCreator)
: base(args.URL, webRequestCreator)
{
_args = args;
}
/// <summary>
/// 创建 UnityWebRequest
/// </summary>
protected override void CreateWebRequest()
{
_downloadHandler = CreateAssetBundleDownloadHandler();
_webRequest = CreateUnityWebRequestGet(URL);
_webRequest.downloadHandler = _downloadHandler;
_webRequest.disposeDownloadHandlerOnDispose = true;
ApplyRequestOptions(_args.Timeout, _args.WatchdogTime, _args.Headers);
}
/// <summary>
/// 请求成功时的回调
/// </summary>
protected override void OnRequestSucceed()
{
AssetBundle assetBundle = _downloadHandler.assetBundle;
if (assetBundle == null)
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] URL: {URL} - AssetBundle object is null";
}
else
{
Result = assetBundle;
}
}
/// <summary>
/// 创建 AssetBundle 下载处理器
/// </summary>
private DownloadHandlerAssetBundle CreateAssetBundleDownloadHandler()
{
DownloadHandlerAssetBundle handler;
if (_args.DisableUnityWebCache)
{
// 禁用 Unity 缓存
handler = new DownloadHandlerAssetBundle(URL, _args.UnityCRC);
}
else
{
// 使用 Unity 缓存(需要 FileHash
// The file hash defining the version of the asset bundle.
Hash128 fileHash = Hash128.Parse(_args.FileHash);
handler = new DownloadHandlerAssetBundle(URL, fileHash, _args.UnityCRC);
}
#if UNITY_2020_3_OR_NEWER
// 禁用自动加载,允许手动控制加载时机
handler.autoLoadAssetBundle = false;
#endif
return handler;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: e01cc308d7179a34281087fafe455b42
guid: 6f635344c08e2b04295a108c2bcc6a40
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,61 @@
using System;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest 字节下载器
/// </summary>
/// <remarks>
/// 将下载内容保存到内存中的字节数组。
/// </remarks>
internal sealed class UnityWebRequestBytesDownloader : UnityWebRequestDownloaderBase, IDownloadBytesRequest
{
private readonly DownloadDataRequestArgs _args;
/// <summary>
/// 下载结果(字节数组)
/// </summary>
public byte[] Result { get; private set; }
/// <summary>
/// 构造字节数组下载器
/// </summary>
/// <param name="args">数据下载参数</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
public UnityWebRequestBytesDownloader(DownloadDataRequestArgs args, UnityWebRequestDelegate webRequestCreator)
: base(args.URL, webRequestCreator)
{
_args = args;
}
/// <summary>
/// 创建 UnityWebRequest
/// </summary>
protected override void CreateWebRequest()
{
var handler = new DownloadHandlerBuffer();
_webRequest = CreateUnityWebRequestGet(URL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
ApplyRequestOptions(_args.Timeout, _args.WatchdogTime, _args.Headers);
}
/// <summary>
/// 请求成功时的回调
/// </summary>
protected override void OnRequestSucceed()
{
var fileData = _webRequest.downloadHandler.data;
if (fileData == null || fileData.Length == 0)
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] URL: {URL} - Download bytes data is null or empty !";
}
else
{
Result = fileData;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 6602c4be2ef295546b7bbb328de8fb0c
guid: 38884a96e8c2df74082d4e059e2e73ed
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,290 @@
using System;
using System.Collections.Generic;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest 下载器基类
/// </summary>
/// <remarks>
/// 封装 UnityWebRequest 的通用下载逻辑,包括状态管理、进度追踪等。
/// 子类只需实现 CreateWebRequest 方法来创建特定类型的下载请求。
/// </remarks>
internal abstract class UnityWebRequestDownloaderBase : IDownloadRequest
{
private readonly UnityWebRequestDelegate _webRequestCreator;
protected UnityWebRequest _webRequest;
// 看门狗相关
private int _watchdogTime = 0;
private bool _watchdogAborted = false;
private long _lastDownloadBytes = -1;
private double _lastGetDataTime;
#region
/// <summary>
/// 请求地址
/// </summary>
public string URL { get; }
/// <summary>
/// 是否完成
/// </summary>
/// <remarks>
/// 每次调用都会主动轮询请求 PollingRequest
/// </remarks>
public bool IsDone
{
get
{
PollingRequest();
return Status == EDownloadRequestStatus.Succeed
|| Status == EDownloadRequestStatus.Failed
|| Status == EDownloadRequestStatus.Aborted;
}
}
/// <summary>
/// 请求状态
/// </summary>
public EDownloadRequestStatus Status { get; protected set; }
/// <summary>
/// 当前下载进度0f - 1f
/// </summary>
public float DownloadProgress { get; private set; }
/// <summary>
/// 当前请求已接收的字节数
/// </summary>
public long DownloadedBytes { get; private set; }
/// <summary>
/// HTTP 返回码
/// </summary>
public long HttpCode { get; private set; }
/// <summary>
/// 错误信息
/// </summary>
public string Error { get; protected set; }
#endregion
/// <summary>
/// 构造下载器基类
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
protected UnityWebRequestDownloaderBase(string url, UnityWebRequestDelegate webRequestCreator)
{
URL = url;
_webRequestCreator = webRequestCreator;
Status = EDownloadRequestStatus.None;
}
/// <summary>
/// 发起请求
/// </summary>
public void SendRequest()
{
if (Status == EDownloadRequestStatus.None)
{
Status = EDownloadRequestStatus.Running;
try
{
CreateWebRequest();
if (_webRequest == null)
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] Created web request is null.";
}
else
{
_webRequest.SendWebRequest();
}
}
catch (Exception ex)
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] Failed to create web request : {ex.Message}";
}
}
}
/// <summary>
/// 轮询请求
/// </summary>
public void PollingRequest()
{
if (Status != EDownloadRequestStatus.Running)
return;
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
CheckWatchdog();
if (_webRequest.isDone == false)
return;
HttpCode = _webRequest.responseCode;
#if UNITY_2020_3_OR_NEWER
bool isSuccess = _webRequest.result == UnityWebRequest.Result.Success;
#else
bool isSuccess = !_webRequest.isNetworkError && !_webRequest.isHttpError;
#endif
if (isSuccess)
{
Status = EDownloadRequestStatus.Succeed;
OnRequestSucceed();
}
else
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] URL: {URL} - 错误: {_webRequest.error}";
OnRequestFailed();
}
// 完成后释放
DisposeWebRequest();
}
/// <summary>
/// 中止请求
/// </summary>
public void AbortRequest()
{
if (Status == EDownloadRequestStatus.None || Status == EDownloadRequestStatus.Running)
{
Status = EDownloadRequestStatus.Aborted;
if (_webRequest != null)
_webRequest.Abort();
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
DisposeWebRequest();
}
/// <summary>
/// 创建 UnityWebRequest子类实现
/// </summary>
protected abstract void CreateWebRequest();
/// <summary>
/// 请求成功时的回调(子类可重写)
/// </summary>
protected virtual void OnRequestSucceed()
{
}
/// <summary>
/// 请求失败时的回调(子类可重写)
/// </summary>
protected virtual void OnRequestFailed()
{
}
/// <summary>
/// 创建 UnityWebRequest GET 请求
/// </summary>
/// <param name="requestUrl">请求地址</param>
/// <returns>UnityWebRequest 实例</returns>
protected UnityWebRequest CreateUnityWebRequestGet(string requestUrl)
{
if (_webRequestCreator != null)
return _webRequestCreator.Invoke(requestUrl);
return new UnityWebRequest(requestUrl, UnityWebRequest.kHttpVerbGET);
}
/// <summary>
/// 创建 UnityWebRequest HEAD 请求
/// </summary>
/// <param name="requestUrl">请求地址</param>
/// <returns>UnityWebRequest 实例</returns>
protected UnityWebRequest CreateUnityWebRequestHead(string requestUrl)
{
return new UnityWebRequest(requestUrl, UnityWebRequest.kHttpVerbHEAD);
}
/// <summary>
/// 应用通用请求参数
/// </summary>
protected void ApplyRequestOptions(int timeout, int watchdogTime, Dictionary<string, string> headers)
{
if (_webRequest == null)
throw new YooInternalException("Web request is null !");
// 设置看门狗超时时间
_watchdogTime = watchdogTime;
// 设置响应的超时时间
if (timeout > 0)
_webRequest.timeout = timeout;
// 设置响应头
if (headers != null)
{
foreach (var header in headers)
{
_webRequest.SetRequestHeader(header.Key, header.Value);
}
}
}
/// <summary>
/// 检测看门狗
/// </summary>
private void CheckWatchdog()
{
if (_watchdogTime == 0)
return;
if (_watchdogAborted)
return;
#if UNITY_2020_3_OR_NEWER
double realtimeSinceStartup = UnityEngine.Time.realtimeSinceStartupAsDouble;
#else
double realtimeSinceStartup = UnityEngine.Time.realtimeSinceStartup;
#endif
if (DownloadedBytes != _lastDownloadBytes)
{
_lastDownloadBytes = DownloadedBytes;
_lastGetDataTime = realtimeSinceStartup;
}
else
{
double deltaTime = realtimeSinceStartup - _lastGetDataTime;
if (deltaTime > _watchdogTime)
{
_watchdogAborted = true;
AbortRequest(); //看门狗终止网络请求
}
}
}
/// <summary>
/// 释放资源
/// </summary>
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9e71e850eded0da43906cb4f7cb75629
guid: 76b0712524ed69542914db8e44fa64fd
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,56 @@
using System;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest 文件下载器
/// </summary>
/// <remarks>
/// 将下载内容保存到本地文件,支持断点续传和追加写入。
/// </remarks>
internal sealed class UnityWebRequestFileDownloader : UnityWebRequestDownloaderBase, IDownloadFileRequest
{
private readonly DownloadFileRequestArgs _args;
/// <summary>
/// 文件保存路径
/// </summary>
public string SavePath
{
get { return _args.SavePath; }
}
/// <summary>
/// 构造文件下载器
/// </summary>
/// <param name="args">文件下载参数</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
public UnityWebRequestFileDownloader(DownloadFileRequestArgs args, UnityWebRequestDelegate webRequestCreator)
: base(args.URL, webRequestCreator)
{
_args = args;
}
/// <summary>
/// 创建 UnityWebRequest
/// </summary>
protected override void CreateWebRequest()
{
var handler = new DownloadHandlerFile(_args.SavePath, _args.AppendToFile);
handler.removeFileOnAbort = _args.RemoveFileOnAbort;
_webRequest = CreateUnityWebRequestGet(URL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
// 断点续传:设置 Range 请求头
if (_args.ResumeFromBytes > 0)
{
_webRequest.SetRequestHeader("Range", $"bytes={_args.ResumeFromBytes}-");
}
ApplyRequestOptions(_args.Timeout, _args.WatchdogTime, _args.Headers);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 375d88bcf5b9a6146adaf98ceb5369f8
guid: 101a4be1bb0a85c4d84ecbf3e74f89e4
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest HEAD 请求下载器
/// </summary>
/// <remarks>
/// 仅获取响应头信息,不下载实际内容。
/// 用于检查资源是否存在、获取资源大小、检查缓存有效性等场景。
/// </remarks>
internal sealed class UnityWebRequestHeadDownloader : UnityWebRequestDownloaderBase, IDownloadHeadRequest
{
// 注意:缓存响应头(因为 WebRequest 释放后无法获取)
private Dictionary<string, string> _cachedResponseHeaders;
private readonly DownloadDataRequestArgs _args;
/// <summary>
/// 获取 ETag 响应头
/// </summary>
public string ETag
{
get { return GetResponseHeader("ETag"); }
}
/// <summary>
/// 获取 Last-Modified 响应头
/// </summary>
public string LastModified
{
get { return GetResponseHeader("Last-Modified"); }
}
/// <summary>
/// 获取 Content-Type 响应头
/// </summary>
public string ContentType
{
get { return GetResponseHeader("Content-Type"); }
}
/// <summary>
/// 获取 Content-Length 响应头
/// 预期下载的总字节数
/// </summary>
public long ContentLength
{
get
{
string contentLengthStr = GetResponseHeader("Content-Length");
if (string.IsNullOrEmpty(contentLengthStr))
return -1;
if (long.TryParse(contentLengthStr, out long contentLength))
{
return contentLength;
}
else
{
return -1;
}
}
}
/// <summary>
/// 构造 HEAD 请求下载器
/// </summary>
/// <param name="args">数据下载参数</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
public UnityWebRequestHeadDownloader(DownloadDataRequestArgs args, UnityWebRequestDelegate webRequestCreator)
: base(args.URL, webRequestCreator)
{
_args = args;
}
/// <summary>
/// 获取响应头信息
/// </summary>
/// <param name="name">响应头名称(不区分大小写)</param>
/// <returns>响应头的值,如果不存在或请求未完成则返回 null</returns>
public string GetResponseHeader(string name)
{
if (_cachedResponseHeaders == null)
return null;
// 注意UnityWebRequest 的响应头 key 是小写的
string lowerName = name.ToLowerInvariant();
if (_cachedResponseHeaders.TryGetValue(lowerName, out string value))
return value;
return null;
}
/// <summary>
/// 创建 UnityWebRequest
/// </summary>
protected override void CreateWebRequest()
{
_webRequest = CreateUnityWebRequestHead(URL);
_webRequest.downloadHandler = null; // HEAD 请求不需要 DownloadHandler
ApplyRequestOptions(_args.Timeout, _args.WatchdogTime, _args.Headers);
}
/// <summary>
/// 请求成功时的回调
/// </summary>
protected override void OnRequestSucceed()
{
var headers = _webRequest.GetResponseHeaders();
if (headers != null)
{
_cachedResponseHeaders = new Dictionary<string, string>(headers.Count, StringComparer.OrdinalIgnoreCase);
foreach (var kvp in headers)
{
string name = kvp.Key.ToLowerInvariant();
string value = kvp.Value;
_cachedResponseHeaders[name] = value;
}
}
}
}
}

View File

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

View File

@@ -0,0 +1,61 @@
using System;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// UnityWebRequest 文本下载器
/// </summary>
/// <remarks>
/// 将下载内容解析为 UTF-8 文本字符串。
/// </remarks>
internal sealed class UnityWebRequestTextDownloader : UnityWebRequestDownloaderBase, IDownloadTextRequest
{
private readonly DownloadDataRequestArgs _args;
/// <summary>
/// 下载结果(文本字符串)
/// </summary>
public string Result { get; private set; }
/// <summary>
/// 构造文本下载器
/// </summary>
/// <param name="args">数据下载参数</param>
/// <param name="webRequestCreator">UnityWebRequest 创建器(可选)</param>
public UnityWebRequestTextDownloader(DownloadDataRequestArgs args, UnityWebRequestDelegate webRequestCreator)
: base(args.URL, webRequestCreator)
{
_args = args;
}
/// <summary>
/// 创建 UnityWebRequest
/// </summary>
protected override void CreateWebRequest()
{
var handler = new DownloadHandlerBuffer();
_webRequest = CreateUnityWebRequestGet(URL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
ApplyRequestOptions(_args.Timeout, _args.WatchdogTime, _args.Headers);
}
/// <summary>
/// 请求成功时的回调
/// </summary>
protected override void OnRequestSucceed()
{
var fileText = _webRequest.downloadHandler.text;
if (string.IsNullOrEmpty(fileText))
{
Status = EDownloadRequestStatus.Failed;
Error = $"[{GetType().Name}] URL: {URL} - Download text data is null or empty";
}
else
{
Result = fileText;
}
}
}
}

View File

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

View File

@@ -0,0 +1,149 @@
using System;
namespace YooAsset
{
/// <summary>
/// 模拟下载器
/// </summary>
/// <remarks>
/// 用于编辑器模式下模拟下载进度,不进行实际网络请求。
/// 根据配置的下载速度模拟进度变化。
/// </remarks>
internal sealed class VirtualFileDownloader : IDownloadFileRequest
{
private readonly DownloadSimulateRequestArgs _args;
private double _lastUpdateTime;
/// <summary>
/// 文件保存路径(模拟下载不需要)
/// </summary>
public string SavePath
{
get { return null; }
}
#region
/// <summary>
/// 请求地址
/// </summary>
public string URL { get; }
/// <summary>
/// 是否完成
/// </summary>
public bool IsDone
{
get
{
PollingRequest();
return Status == EDownloadRequestStatus.Succeed
|| Status == EDownloadRequestStatus.Failed
|| Status == EDownloadRequestStatus.Aborted;
}
}
/// <summary>
/// 请求状态
/// </summary>
public EDownloadRequestStatus Status { get; private set; }
/// <summary>
/// 当前下载进度0f - 1f
/// </summary>
public float DownloadProgress { get; private set; }
/// <summary>
/// 当前请求已接收的字节数
/// </summary>
public long DownloadedBytes { get; private set; }
/// <summary>
/// HTTP 返回码(模拟固定返回 200
/// </summary>
public long HttpCode { get; private set; }
/// <summary>
/// 错误信息
/// </summary>
public string Error { get; private set; }
#endregion
/// <summary>
/// 构造模拟下载器
/// </summary>
/// <param name="args">模拟下载参数</param>
public VirtualFileDownloader(DownloadSimulateRequestArgs args)
{
_args = args;
Status = EDownloadRequestStatus.None;
}
/// <summary>
/// 发起请求
/// </summary>
public void SendRequest()
{
if (Status == EDownloadRequestStatus.None)
{
Status = EDownloadRequestStatus.Running;
_lastUpdateTime = GetUnityEngineRealtime();
}
}
/// <summary>
/// 轮询请求
/// </summary>
public void PollingRequest()
{
if (Status != EDownloadRequestStatus.Running)
return;
double currentTime = GetUnityEngineRealtime();
double deltaTime = currentTime - _lastUpdateTime;
_lastUpdateTime = currentTime;
// 计算本帧下载的字节数
long downloadBytes = (long)(_args.DownloadSpeed * deltaTime);
DownloadedBytes += downloadBytes;
if (_args.FileSize > 0)
DownloadProgress = (float)DownloadedBytes / _args.FileSize;
// 检查是否完成
if (DownloadedBytes >= _args.FileSize)
{
HttpCode = 200;
DownloadProgress = 1f;
DownloadedBytes = _args.FileSize;
Status = EDownloadRequestStatus.Succeed;
}
}
/// <summary>
/// 中止请求
/// </summary>
public void AbortRequest()
{
if (Status == EDownloadRequestStatus.None || Status == EDownloadRequestStatus.Running)
{
Status = EDownloadRequestStatus.Aborted;
}
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
}
private double GetUnityEngineRealtime()
{
#if UNITY_2020_3_OR_NEWER
return UnityEngine.Time.realtimeSinceStartupAsDouble;
#else
return = UnityEngine.Time.realtimeSinceStartup;
#endif
}
}
}

View File

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

View File

@@ -1,118 +0,0 @@

namespace YooAsset
{
/// <summary>
/// 下载器结束
/// </summary>
public struct DownloaderFinishData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 是否成功
/// </summary>
public bool Succeed;
}
/// <summary>
/// 下载器相关的更新数据
/// </summary>
public struct DownloadUpdateData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载进度 (0-1f)
/// </summary>
public float Progress;
/// <summary>
/// 下载文件总数
/// </summary>
public int TotalDownloadCount;
/// <summary>
/// 当前完成的下载文件数量
/// </summary>
public int CurrentDownloadCount;
/// <summary>
/// 下载数据总大小(单位:字节)
/// </summary>
public long TotalDownloadBytes;
/// <summary>
/// 当前完成的下载数据大小(单位:字节)
/// </summary>
public long CurrentDownloadBytes;
}
/// <summary>
/// 下载器相关的错误数据
/// </summary>
public struct DownloadErrorData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载失败的文件名称
/// </summary>
public string FileName;
/// <summary>
/// 错误信息
/// </summary>
public string ErrorInfo;
}
/// <summary>
/// 下载器相关的文件数据
/// </summary>
public struct DownloadFileData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载的文件名称
/// </summary>
public string FileName;
/// <summary>
/// 下载的文件大小
/// </summary>
public long FileSize;
}
/// <summary>
/// 导入文件的信息
/// </summary>
public struct ImportFileInfo
{
/// <summary>
/// 本地文件路径
/// </summary>
public string FilePath;
/// <summary>
/// 资源包名称
/// </summary>
public string BundleName;
/// <summary>
/// 资源包GUID
/// </summary>
public string BundleGUID;
}
}

View File

@@ -0,0 +1,443 @@
using System.Collections.Generic;
namespace YooAsset
{
/// <summary>
/// 下载器结束
/// </summary>
public struct DownloaderFinishData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 是否成功
/// </summary>
public bool Succeed;
}
/// <summary>
/// 下载器相关的更新数据
/// </summary>
public struct DownloadUpdateData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载进度 (0-1f)
/// </summary>
public float Progress;
/// <summary>
/// 下载文件总数
/// </summary>
public int TotalDownloadCount;
/// <summary>
/// 当前完成的下载文件数量
/// </summary>
public int CurrentDownloadCount;
/// <summary>
/// 下载数据总大小(单位:字节)
/// </summary>
public long TotalDownloadBytes;
/// <summary>
/// 当前完成的下载数据大小(单位:字节)
/// </summary>
public long CurrentDownloadBytes;
}
/// <summary>
/// 下载器相关的错误数据
/// </summary>
public struct DownloadErrorData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载失败的文件名称
/// </summary>
public string FileName;
/// <summary>
/// 错误信息
/// </summary>
public string ErrorInfo;
}
/// <summary>
/// 下载器相关的文件数据
/// </summary>
public struct DownloadFileData
{
/// <summary>
/// 所属包裹名称
/// </summary>
public string PackageName;
/// <summary>
/// 下载的文件名称
/// </summary>
public string FileName;
/// <summary>
/// 下载的文件大小
/// </summary>
public long FileSize;
}
/// <summary>
/// 导入文件的信息
/// </summary>
public struct ImportFileInfo
{
/// <summary>
/// 本地文件路径
/// </summary>
public string FilePath;
/// <summary>
/// 资源包名称
/// </summary>
public string BundleName;
/// <summary>
/// 资源包GUID
/// </summary>
public string BundleGUID;
}
/// <summary>
/// 下载请求状态
/// </summary>
internal enum EDownloadRequestStatus
{
/// <summary>
/// 未开始
/// </summary>
None,
/// <summary>
/// 进行中
/// </summary>
Running,
/// <summary>
/// 已成功
/// </summary>
Succeed,
/// <summary>
/// 已失败
/// </summary>
Failed,
/// <summary>
/// 已中止
/// </summary>
Aborted,
}
/// <summary>
/// 文件下载请求参数
/// </summary>
/// <remarks>
/// 用于将下载内容保存到本地文件的请求配置。
/// 支持断点续传和追加写入模式。
/// </remarks>
internal struct DownloadFileRequestArgs
{
/// <summary>
/// 请求地址
/// </summary>
public readonly string URL;
/// <summary>
/// 响应的超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 当 Timeout 设置为 0 时,不应用超时。
/// 设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </remarks>
public readonly int Timeout;
/// <summary>
/// 看门狗超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 用于监控下载任务的数据接收情况。
/// 规则说明:
/// 1. 当设置值为 0 时,表示禁用看门狗监控。
/// 2. 每次接收到下载数据时,看门狗计时器会重置。
/// 3. 若在设定的时间范围内未收到任何数据,任务将被自动终止。
/// </remarks>
public readonly int WatchdogTime;
/// <summary>
/// 文件保存路径
/// </summary>
public readonly string SavePath;
/// <summary>
/// 是否追加写入文件
/// </summary>
/// <remarks>
/// 配合 ResumeFromBytes 使用,用于断点续传场景。
/// </remarks>
public readonly bool AppendToFile;
/// <summary>
/// 中止请求时是否删除目标文件
/// </summary>
public readonly bool RemoveFileOnAbort;
/// <summary>
/// 断点续传的起始字节(小于等于 0 表示不启用)
/// </summary>
/// <remarks>
/// 推荐由后端自动设置 Range 请求头:"bytes={ResumeFromBytes}-"。
/// </remarks>
public readonly long ResumeFromBytes;
/// <summary>
/// 自定义请求头(可选)
/// </summary>
public Dictionary<string, string> Headers;
/// <summary>
/// 构造文件下载请求参数
/// </summary>
public DownloadFileRequestArgs(
string url,
string savePath,
int timeout,
int watchdogTime,
bool appendToFile = false,
bool removeFileOnAbort = true,
long resumeFromBytes = 0)
{
URL = url;
SavePath = savePath;
Timeout = timeout;
WatchdogTime = watchdogTime;
AppendToFile = appendToFile;
RemoveFileOnAbort = removeFileOnAbort;
ResumeFromBytes = resumeFromBytes;
Headers = null;
}
/// <summary>
/// 添加请求头数据
/// </summary>
public void AddRequestHeader(string name, string value)
{
if (Headers == null)
Headers = new Dictionary<string, string>(10);
Headers.Add(name, value);
}
}
/// <summary>
/// 数据下载请求参数(通用)
/// </summary>
/// <remarks>
/// 用于下载到内存的请求配置。
/// 可用于字节数组bytes或文本text下载。
/// </remarks>
internal struct DownloadDataRequestArgs
{
/// <summary>
/// 请求地址
/// </summary>
public readonly string URL;
/// <summary>
/// 响应的超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 当 Timeout 设置为 0 时,不应用超时。
/// 设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </remarks>
public readonly int Timeout;
/// <summary>
/// 看门狗超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 用于监控下载任务的数据接收情况。
/// 规则说明:
/// 1. 当设置值为 0 时,表示禁用看门狗监控。
/// 2. 每次接收到下载数据时,看门狗计时器会重置。
/// 3. 若在设定的时间范围内未收到任何数据,任务将被自动终止。
/// </remarks>
public readonly int WatchdogTime;
/// <summary>
/// 自定义请求头(可选)
/// </summary>
public Dictionary<string, string> Headers;
/// <summary>
/// 构造数据下载请求参数
/// </summary>
/// <param name="url">请求地址</param>
/// <param name="options">通用请求参数</param>
public DownloadDataRequestArgs(string url, int timeout, int watchdogTime)
{
URL = url;
Timeout = timeout;
WatchdogTime = watchdogTime;
Headers = null;
}
/// <summary>
/// 添加请求头数据
/// </summary>
public void AddRequestHeader(string name, string value)
{
if (Headers == null)
Headers = new Dictionary<string, string>(10);
Headers.Add(name, value);
}
}
/// <summary>
/// AssetBundle 下载请求参数
/// </summary>
/// <remarks>
/// 用于下载并加载 Unity AssetBundle 的请求配置。
/// 支持 Unity 内置缓存机制和 CRC 校验。
/// </remarks>
internal struct DownloadAssetBundleRequestArgs
{
/// <summary>
/// 请求地址
/// </summary>
public readonly string URL;
/// <summary>
/// 响应的超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 当 Timeout 设置为 0 时,不应用超时。
/// 设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </remarks>
public readonly int Timeout;
/// <summary>
/// 看门狗超时时间(单位:秒)
/// </summary>
/// <remarks>
/// 用于监控下载任务的数据接收情况。
/// 规则说明:
/// 1. 当设置值为 0 时,表示禁用看门狗监控。
/// 2. 每次接收到下载数据时,看门狗计时器会重置。
/// 3. 若在设定的时间范围内未收到任何数据,任务将被自动终止。
/// </remarks>
public readonly int WatchdogTime;
/// <summary>
/// 禁用 Unity 的网络缓存
/// </summary>
public readonly bool DisableUnityWebCache;
/// <summary>
/// AssetBundle 文件哈希(用于 UnityWebRequest 的缓存)
/// </summary>
/// <remarks>
/// 仅当 DisableUnityWebCache 为 false 时需要。
/// </remarks>
public readonly string FileHash;
/// <summary>
/// Unity CRC 校验值
/// </summary>
public readonly uint UnityCRC;
/// <summary>
/// 自定义请求头(可选)
/// </summary>
public Dictionary<string, string> Headers;
/// <summary>
/// 构造 AssetBundle 下载请求参数
/// </summary>
public DownloadAssetBundleRequestArgs(
string url,
int timeout,
int watchdogTime,
bool disableUnityWebCache = true,
string fileHash = null,
uint unityCrc = 0)
{
URL = url;
Timeout = timeout;
WatchdogTime = watchdogTime;
DisableUnityWebCache = disableUnityWebCache;
FileHash = fileHash;
UnityCRC = unityCrc;
Headers = null;
}
/// <summary>
/// 添加请求头数据
/// </summary>
public void AddRequestHeader(string name, string value)
{
if (Headers == null)
Headers = new Dictionary<string, string>(10);
Headers.Add(name, value);
}
}
/// <summary>
/// 模拟下载请求参数
/// </summary>
/// <remarks>
/// 用于编辑器模式下模拟下载进度,不进行实际网络请求。
/// </remarks>
internal struct DownloadSimulateRequestArgs
{
/// <summary>
/// 请求地址(仅用于标识)
/// </summary>
public readonly string URL;
/// <summary>
/// 模拟的文件大小(字节)
/// </summary>
public readonly long FileSize;
/// <summary>
/// 模拟的下载速度(字节/秒)
/// </summary>
/// <remarks>
/// 用于计算模拟的下载进度。
/// </remarks>
public readonly long DownloadSpeed;
/// <summary>
/// 构造模拟下载请求参数
/// </summary>
/// <param name="url">请求地址(仅用于标识)</param>
/// <param name="fileSize">模拟的文件大小(字节)</param>
/// <param name="downloadSpeed">模拟的下载速度(字节/秒),默认 1MB/s</param>
public DownloadSimulateRequestArgs(string url, long fileSize, long downloadSpeed = 1024 * 1024)
{
URL = url;
FileSize = fileSize;
DownloadSpeed = downloadSpeed > 0 ? downloadSpeed : 1024 * 1024;
}
}
}

View File

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

View File

@@ -94,8 +94,12 @@ namespace YooAsset
public static bool IsRequestLocalFile(string url)
{
//TODO UNITY_STANDALONE_OSX平台目前无法确定
// 本地文件传输协议
if (url.StartsWith("file:"))
return true;
// JAR文件协议
if (url.StartsWith("jar:file:"))
return true;

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4630dac2050606043bb146325fdce6ad
guid: d724672f4d6f24b4db435d10eef6c40d
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,78 @@
using System;
namespace YooAsset
{
/// <summary>
/// 下载后台接口
/// </summary>
/// <remarks>
/// 不同网络库UnityWebRequest / BestHTTP / 自研)实现该接口,用于创建具体下载请求。
/// 每个后台实例是独立的,不共享全局状态。
/// </remarks>
internal interface IDownloadBackend : IDisposable
{
/// <summary>
/// 后台名称(用于日志与调试)
/// </summary>
string Name { get; }
/// <summary>
/// 驱动更新
/// </summary>
/// <remarks>
/// 部分第三方网络库需要在 Unity 主线程中周期性调用 Update 进行驱动。
/// 不需要驱动的后台可实现为空方法。
/// </remarks>
void Update();
/// <summary>
/// 创建 HEAD 请求
/// </summary>
/// <remarks>
/// 仅获取响应头信息,不下载实际内容。
/// 用于检查资源是否存在、获取资源大小、检查缓存有效性等场景。
/// </remarks>
/// <param name="args">数据请求参数</param>
/// <returns>HEAD 请求实例</returns>
IDownloadHeadRequest CreateHeadRequest(DownloadDataRequestArgs args);
/// <summary>
/// 创建文件下载请求
/// </summary>
/// <param name="args">文件下载参数</param>
/// <returns>文件下载请求实例</returns>
IDownloadFileRequest CreateFileRequest(DownloadFileRequestArgs args);
/// <summary>
/// 创建内存下载请求(字节数组)
/// </summary>
/// <param name="args">数据下载参数</param>
/// <returns>字节下载请求实例</returns>
IDownloadBytesRequest CreateBytesRequest(DownloadDataRequestArgs args);
/// <summary>
/// 创建文本下载请求
/// </summary>
/// <param name="args">数据下载参数</param>
/// <returns>文本下载请求实例</returns>
IDownloadTextRequest CreateTextRequest(DownloadDataRequestArgs args);
/// <summary>
/// 创建 AssetBundle 下载请求
/// </summary>
/// <param name="args">AssetBundle 下载参数</param>
/// <returns>AssetBundle 下载请求实例</returns>
IDownloadAssetBundleRequest CreateAssetBundleRequest(DownloadAssetBundleRequestArgs args);
/// <summary>
/// 创建模拟下载请求
/// </summary>
/// <remarks>
/// 用于编辑器模式下模拟下载进度,不进行实际网络请求。
/// 可用于测试下载流程和 UI 展示。
/// </remarks>
/// <param name="args">模拟下载参数</param>
/// <returns>模拟下载请求实例</returns>
IDownloadFileRequest CreateSimulateRequest(DownloadSimulateRequestArgs args);
}
}

View File

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

View File

@@ -0,0 +1,194 @@
using System;
namespace YooAsset
{
/// <summary>
/// 可轮询的下载请求接口
/// </summary>
/// <remarks>
/// 上层通常会在每帧轮询 Status IsDone并在完成后读取结果。
/// </remarks>
internal interface IDownloadRequest : IDisposable
{
/// <summary>
/// 请求地址
/// </summary>
string URL { get; }
/// <summary>
/// 是否完成(成功/失败/中止)
/// </summary>
bool IsDone { get; }
/// <summary>
/// 请求状态
/// </summary>
EDownloadRequestStatus Status { get; }
/// <summary>
/// 当前下载进度0f - 1f
/// </summary>
/// <remarks>
/// 部分情况下无法准确获取总长度,可返回 0。
/// </remarks>
float DownloadProgress { get; }
/// <summary>
/// 当前请求已接收的字节数
/// </summary>
/// <remarks>
/// 断点续传场景下,该值仅表示"本次请求新增下载的字节数",不包含已存在的本地文件长度。
/// </remarks>
long DownloadedBytes { get; }
/// <summary>
/// HTTP 返回码
/// </summary>
/// <remarks>
/// 非 HTTP 协议可返回 0。使用 long 类型以兼容各种协议的返回码。
/// </remarks>
long HttpCode { get; }
/// <summary>
/// 错误信息
/// </summary>
/// <remarks>
/// 失败时不为空。
/// </remarks>
string Error { get; }
/// <summary>
/// 发起请求
/// </summary>
void SendRequest();
/// <summary>
/// 轮询请求
/// </summary>
void PollingRequest();
/// <summary>
/// 中止请求
/// </summary>
void AbortRequest();
}
/// <summary>
/// HEAD 请求接口(仅获取响应头)
/// </summary>
/// <remarks>
/// 用于检查资源是否存在、获取资源大小、检查缓存有效性等场景。
/// 不下载实际内容,仅获取响应头信息。
/// </remarks>
internal interface IDownloadHeadRequest : IDownloadRequest
{
/// <summary>
/// 获取 ETag 响应头
/// </summary>
/// <remarks>
/// 用于缓存验证,如果服务器未返回则为 null。
/// </remarks>
string ETag { get; }
/// <summary>
/// 获取 Last-Modified 响应头
/// </summary>
/// <remarks>
/// 资源最后修改时间,如果服务器未返回则为 null。
/// </remarks>
string LastModified { get; }
/// <summary>
/// 获取 Content-Type 响应头
/// </summary>
/// <remarks>
/// 资源的 MIME 类型,如果服务器未返回则为 null。
/// </remarks>
string ContentType { get; }
/// <summary>
/// 预期下载的总字节数Content-Length
/// </summary>
/// <remarks>
/// 从响应头 Content-Length 获取。
/// 如果服务器未返回或请求未完成,返回 -1。
/// 用于更准确的进度计算。
/// </remarks>
long ContentLength { get; }
/// <summary>
/// 获取响应头信息
/// </summary>
/// <param name="name">响应头名称(不区分大小写)</param>
/// <returns>响应头的值,如果不存在或请求未完成则返回 null</returns>
/// <remarks>
/// 常用响应头Content-Length、Content-Type、ETag、Last-Modified 等。
/// </remarks>
string GetResponseHeader(string name);
}
/// <summary>
/// 文件下载请求接口
/// </summary>
/// <remarks>
/// 将下载内容保存到指定的本地文件路径。
/// </remarks>
internal interface IDownloadFileRequest : IDownloadRequest
{
/// <summary>
/// 文件保存路径
/// </summary>
string SavePath { get; }
}
/// <summary>
/// 内存下载请求接口(字节数组)
/// </summary>
/// <remarks>
/// 将下载内容保存到内存中的字节数组。
/// </remarks>
internal interface IDownloadBytesRequest : IDownloadRequest
{
/// <summary>
/// 下载结果(字节数组)
/// </summary>
/// <remarks>
/// 仅在请求成功时可用,失败时为 null。
/// </remarks>
byte[] Result { get; }
}
/// <summary>
/// 文本下载请求接口
/// </summary>
/// <remarks>
/// 将下载内容解析为 UTF-8 文本字符串。
/// </remarks>
internal interface IDownloadTextRequest : IDownloadRequest
{
/// <summary>
/// 下载结果(文本字符串)
/// </summary>
/// <remarks>
/// 仅在请求成功时可用,失败时为 null。
/// </remarks>
string Result { get; }
}
/// <summary>
/// AssetBundle 下载请求接口
/// </summary>
/// <remarks>
/// 下载并加载 Unity AssetBundle 资源包。
/// </remarks>
internal interface IDownloadAssetBundleRequest : IDownloadRequest
{
/// <summary>
/// 下载结果AssetBundle 对象)
/// </summary>
/// <remarks>
/// 仅在请求成功时可用,失败时为 null。
/// </remarks>
UnityEngine.AssetBundle Result { get; }
}
}

View File

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

View File

@@ -1,113 +0,0 @@
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityAssetBundleRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
Done,
}
private UnityWebRequestAsyncOperation _requestOperation;
private DownloadHandlerAssetBundle _downloadhandler;
private readonly PackageBundle _packageBundle;
private readonly bool _disableUnityWebCache;
private ESteps _steps = ESteps.None;
/// <summary>
/// 请求结果
/// </summary>
public AssetBundle Result { private set; get; }
internal UnityAssetBundleRequestOperation(PackageBundle packageBundle, bool disableUnityWebCache, string url) : base(url)
{
_packageBundle = packageBundle;
_disableUnityWebCache = disableUnityWebCache;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CreateRequest)
{
CreateWebRequest();
_steps = ESteps.Download;
}
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = _requestOperation.progress;
if (_requestOperation.isDone == false)
return;
if (CheckRequestResult())
{
AssetBundle assetBundle = _downloadhandler.assetBundle;
if (assetBundle == null)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"URL : {_requestURL} Download handler asset bundle object is null !";
}
else
{
_steps = ESteps.Done;
Result = assetBundle;
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
}
private void CreateWebRequest()
{
_downloadhandler = CreateWebDownloadHandler();
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.downloadHandler = _downloadhandler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_requestOperation = _webRequest.SendWebRequest();
}
private DownloadHandlerAssetBundle CreateWebDownloadHandler()
{
if (_disableUnityWebCache)
{
var downloadhandler = new DownloadHandlerAssetBundle(_requestURL, _packageBundle.UnityCRC);
#if UNITY_2020_3_OR_NEWER
downloadhandler.autoLoadAssetBundle = false;
#endif
return downloadhandler;
}
else
{
// 注意:优先从浏览器缓存里获取文件
// The file hash defining the version of the asset bundle.
Hash128 fileHash = Hash128.Parse(_packageBundle.FileHash);
var downloadhandler = new DownloadHandlerAssetBundle(_requestURL, fileHash, _packageBundle.UnityCRC);
#if UNITY_2020_3_OR_NEWER
downloadhandler.autoLoadAssetBundle = false;
#endif
return downloadhandler;
}
}
}
}

View File

@@ -1,65 +0,0 @@
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityVirtualBundleRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
Download,
Done,
}
private readonly PackageBundle _bundle;
private readonly int _downloadSpeed;
private ESteps _steps = ESteps.None;
internal UnityVirtualBundleRequestOperation(PackageBundle packageBundle, int downloadSpeed, string url) : base(url)
{
_bundle = packageBundle;
_downloadSpeed = downloadSpeed;
}
internal override void InternalStart()
{
_steps = ESteps.Download;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Download)
{
// 模拟下载进度
float progress = 0;
if (DownloadedBytes > 0)
progress = DownloadedBytes / _bundle.FileSize;
long downloadBytes = (long)((double)_downloadSpeed * Time.deltaTime);
Progress = progress;
DownloadProgress = progress;
DownloadedBytes += downloadBytes;
if (DownloadedBytes < _bundle.FileSize)
return;
Progress = 1f;
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Try load bundle {_bundle.BundleName} from remote !";
}
}
}
}

View File

@@ -1,89 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityWebCacheRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
Done,
}
private UnityWebRequestAsyncOperation _requestOperation;
private readonly Dictionary<string, string> _headers = new Dictionary<string, string>();
private ESteps _steps = ESteps.None;
internal UnityWebCacheRequestOperation(string url) : base(url)
{
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CreateRequest)
{
CreateWebRequest();
_steps = ESteps.Download;
}
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = _requestOperation.progress;
if (_requestOperation.isDone == false)
return;
if (CheckRequestResult())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
}
/// <summary>
/// 设置请求头信息
/// </summary>
public void SetRequestHeader(string name, string value)
{
_headers.Add(name, value);
}
private void CreateWebRequest()
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.disposeDownloadHandlerOnDispose = true;
// 设置消息头
foreach (var keyValuePair in _headers)
{
string name = keyValuePair.Key;
string value = keyValuePair.Value;
_webRequest.SetRequestHeader(name, value);
}
_requestOperation = _webRequest.SendWebRequest();
}
}
}

View File

@@ -1,96 +0,0 @@
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityWebDataRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
Done,
}
private UnityWebRequestAsyncOperation _requestOperation;
private ESteps _steps = ESteps.None;
/// <summary>
/// 响应的超时时间单位在经过Timeout的秒数后尝试中止。
/// 注意当Timeout设置为0时不会应用超时。
/// 注意设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </summary>
private readonly int _timeout;
/// <summary>
/// 请求结果
/// </summary>
public byte[] Result { private set; get; }
internal UnityWebDataRequestOperation(string url, int timeout) : base(url)
{
_timeout = timeout;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CreateRequest)
{
CreateWebRequest();
_steps = ESteps.Download;
}
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = _requestOperation.progress;
if (_requestOperation.isDone == false)
return;
if (CheckRequestResult())
{
var fileData = _webRequest.downloadHandler.data;
if (fileData == null || fileData.Length == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"URL : {_requestURL} Download handler data is null or empty !";
}
else
{
_steps = ESteps.Done;
Result = fileData;
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
}
private void CreateWebRequest()
{
DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.timeout = _timeout;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_requestOperation = _webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -1,83 +0,0 @@
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityWebFileRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
Done,
}
private UnityWebRequestAsyncOperation _requestOperation;
private readonly string _fileSavePath;
private ESteps _steps = ESteps.None;
/// <summary>
/// 响应的超时时间单位在经过Timeout的秒数后尝试中止。
/// 注意当Timeout设置为0时不会应用超时。
/// 注意设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </summary>
private readonly int _timeout;
internal UnityWebFileRequestOperation(string url, string fileSavePath, int timeout) : base(url)
{
_fileSavePath = fileSavePath;
_timeout = timeout;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CreateRequest)
{
CreateWebRequest();
_steps = ESteps.Download;
}
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = _requestOperation.progress;
if (_requestOperation.isDone == false)
return;
if (CheckRequestResult())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
}
private void CreateWebRequest()
{
DownloadHandlerFile handler = new DownloadHandlerFile(_fileSavePath);
handler.removeFileOnAbort = true;
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.timeout = _timeout;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_requestOperation = _webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -1,98 +0,0 @@
using System;
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal abstract class UnityWebRequestOperation : AsyncOperationBase
{
protected UnityWebRequest _webRequest;
protected readonly string _requestURL;
private bool _isAbort = false;
/// <summary>
/// HTTP返回码
/// </summary>
public long HttpCode { private set; get; }
/// <summary>
/// 当前下载的字节数
/// </summary>
public long DownloadedBytes { protected set; get; }
/// <summary>
/// 当前下载进度0f - 1f
/// </summary>
public float DownloadProgress { protected set; get; }
/// <summary>
/// 请求的URL地址
/// </summary>
public string URL
{
get { return _requestURL; }
}
internal UnityWebRequestOperation(string url)
{
_requestURL = url;
}
internal override void InternalAbort()
{
//TODO
// 1. 编辑器下停止运行游戏的时候主动终止下载任务
// 2. 真机上销毁包裹的时候主动终止下载任务
if (_isAbort == false)
{
if (_webRequest != null)
{
_webRequest.Abort();
_isAbort = true;
}
}
}
/// <summary>
/// 释放下载器
/// </summary>
protected void DisposeRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
/// <summary>
/// 检测请求结果
/// </summary>
protected bool CheckRequestResult()
{
HttpCode = _webRequest.responseCode;
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
Error = $"URL : {_requestURL} Error : {_webRequest.error}";
return false;
}
else
{
return true;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
Error = $"URL : {_requestURL} Error : {_webRequest.error}";
return false;
}
else
{
return true;
}
#endif
}
}
}

View File

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

View File

@@ -1,96 +0,0 @@
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal class UnityWebTextRequestOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
Done,
}
private UnityWebRequestAsyncOperation _requestOperation;
private ESteps _steps = ESteps.None;
/// <summary>
/// 响应的超时时间单位在经过Timeout的秒数后尝试中止。
/// 注意当Timeout设置为0时不会应用超时。
/// 注意设置的超时值可能应用于Android上的每个URL重定向这可能会导致响应时间增加。
/// </summary>
private readonly int _timeout;
/// <summary>
/// 请求结果
/// </summary>
public string Result { private set; get; }
internal UnityWebTextRequestOperation(string url, int timeout) : base(url)
{
_timeout = timeout;
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CreateRequest)
{
CreateWebRequest();
_steps = ESteps.Download;
}
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = _requestOperation.progress;
if (_requestOperation.isDone == false)
return;
if (CheckRequestResult())
{
var fileText = _webRequest.downloadHandler.text;
if (string.IsNullOrEmpty(fileText))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"URL : {_requestURL} Download handler text is null or empty !";
}
else
{
_steps = ESteps.Done;
Result = fileText;
Status = EOperationStatus.Succeed;
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
}
private void CreateWebRequest()
{
DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.timeout = _timeout;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_requestOperation = _webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -25,6 +25,11 @@ namespace YooAsset
protected IFileSystem _unpackFileSystem;
protected string _packageRoot;
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
@@ -66,7 +71,7 @@ namespace YooAsset
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验最大并发数
/// </summary>
public int FileVerifyMaxConcurrency { private set; get; } = int.MaxValue;
public int FileVerifyMaxConcurrency { private set; get; } = 32;
/// <summary>
/// 自定义参数:数据文件追加文件格式
@@ -156,6 +161,13 @@ namespace YooAsset
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}";
@@ -225,6 +237,10 @@ namespace YooAsset
else
_packageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(DownloadSystemHelper.UnityWebRequestCreater);
// 创建解压文件系统
var remoteServices = new DefaultUnpackRemoteServices(_packageRoot);
_unpackFileSystem = new DefaultUnpackFileSystem();

View File

@@ -78,7 +78,7 @@ namespace YooAsset
string packageVersion = _requestBuildinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageHashDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuildinPackageHashFilePath(packageVersion);
_copyBuildinHashFileOp = new CopyBuildinFileOperation(sourceFilePath, destFilePath);
_copyBuildinHashFileOp = new CopyBuildinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuildinHashFileOp.StartOperation();
AddChildOperation(_copyBuildinHashFileOp);
}
@@ -106,7 +106,7 @@ namespace YooAsset
string packageVersion = _requestBuildinPackageVersionOp.PackageVersion;
string destFilePath = GetCopyPackageManifestDestPath(packageVersion);
string sourceFilePath = _fileSystem.GetBuildinPackageManifestFilePath(packageVersion);
_copyBuildinManifestFileOp = new CopyBuildinFileOperation(sourceFilePath, destFilePath);
_copyBuildinManifestFileOp = new CopyBuildinFileOperation(_fileSystem, sourceFilePath, destFilePath);
_copyBuildinManifestFileOp.StartOperation();
AddChildOperation(_copyBuildinManifestFileOp);
}

View File

@@ -14,13 +14,15 @@ namespace YooAsset
Done,
}
private UnityWebFileRequestOperation _webFileRequestOp;
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _sourceFilePath;
private readonly string _destFilePath;
private IDownloadFileRequest _webFileRequestOp;
private ESteps _steps = ESteps.None;
public CopyBuildinFileOperation(string sourceFilePath, string destFilePath)
public CopyBuildinFileOperation(DefaultBuildinFileSystem fileSystem, string sourceFilePath, string destFilePath)
{
_fileSystem = fileSystem;
_sourceFilePath = sourceFilePath;
_destFilePath = destFilePath;
}
@@ -76,16 +78,15 @@ namespace YooAsset
if (_webFileRequestOp == null)
{
string url = DownloadSystemHelper.ConvertToWWWPath(_sourceFilePath);
_webFileRequestOp = new UnityWebFileRequestOperation(url, _destFilePath, 60);
_webFileRequestOp.StartOperation();
AddChildOperation(_webFileRequestOp);
var args = new DownloadFileRequestArgs(url, _destFilePath, 60, 0);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
_webFileRequestOp.UpdateOperation();
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EOperationStatus.Succeed)
if (_webFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@@ -15,7 +15,7 @@ namespace YooAsset
}
private readonly DefaultBuildinFileSystem _fileSystem;
private UnityWebDataRequestOperation _webDataRequestOp;
private IDownloadBytesRequest _webDataRequestOp;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
@@ -57,16 +57,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetCatalogBinaryFileLoadPath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webDataRequestOp = new UnityWebDataRequestOperation(url, 60);
_webDataRequestOp.StartOperation();
AddChildOperation(_webDataRequestOp);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
_webDataRequestOp.UpdateOperation();
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EOperationStatus.Succeed)
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_fileData = _webDataRequestOp.Result;
_steps = ESteps.LoadCatalog;

View File

@@ -17,7 +17,7 @@ namespace YooAsset
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly string _packageHash;
private UnityWebDataRequestOperation _webDataRequestOp;
private IDownloadBytesRequest _webDataRequestOp;
private DeserializeManifestOperation _deserializer;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
@@ -63,16 +63,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetBuildinPackageManifestFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webDataRequestOp = new UnityWebDataRequestOperation(url, 60);
_webDataRequestOp.StartOperation();
AddChildOperation(_webDataRequestOp);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
_webDataRequestOp.UpdateOperation();
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EOperationStatus.Succeed)
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_fileData = _webDataRequestOp.Result;
_steps = ESteps.VerifyFileData;

View File

@@ -15,7 +15,7 @@ namespace YooAsset
private readonly DefaultBuildinFileSystem _fileSystem;
private readonly string _packageVersion;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
@@ -58,16 +58,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetBuildinPackageHashFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webTextRequestOp = new UnityWebTextRequestOperation(url, 60);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageHash = _webTextRequestOp.Result;
_steps = ESteps.CheckResult;

View File

@@ -14,7 +14,7 @@ namespace YooAsset
}
private readonly DefaultBuildinFileSystem _fileSystem;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
@@ -56,16 +56,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetBuildinPackageVersionFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webTextRequestOp = new UnityWebTextRequestOperation(url, 60);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, 60, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
_steps = ESteps.CheckResult;

View File

@@ -23,10 +23,14 @@ namespace YooAsset
protected string _cacheManifestFilesRoot;
/// <summary>
/// 下载中心
/// 说明:当异步操作任务终止的时候,所有下载子任务都会一同被终止!
/// 下载调度器
/// </summary>
public DownloadCenterOperation DownloadCenter { set; get; }
public DownloadSchedulerOperation DownloadScheduler { set; get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
@@ -105,7 +109,7 @@ namespace YooAsset
/// <summary>
/// 自定义参数:下载任务的看门狗机制监控时间
/// </summary>
public int DownloadWatchDogTime { private set; get; } = int.MaxValue;
public int DownloadWatchDogTime { private set; get; } = 0;
/// <summary>
/// 自定义参数:启用断点续传的最小尺寸
@@ -288,7 +292,7 @@ namespace YooAsset
else if (name == FileSystemParametersDefine.DOWNLOAD_WATCH_DOG_TIME)
{
int convertValue = Convert.ToInt32(value);
DownloadWatchDogTime = Mathf.Clamp(convertValue, 1, int.MaxValue);
DownloadWatchDogTime = Mathf.Clamp(convertValue, 0, int.MaxValue);
}
else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE)
{
@@ -327,13 +331,17 @@ namespace YooAsset
_cacheBundleFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.BundleFilesFolderName);
_cacheManifestFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.ManifestFilesFolderName);
_tempFilesRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.TempFilesFolderName);
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(DownloadSystemHelper.UnityWebRequestCreater);
}
public virtual void OnDestroy()
{
if (DownloadCenter != null)
if (DownloadScheduler != null)
{
DownloadCenter.AbortOperation();
DownloadCenter = null;
DownloadScheduler.Dispose();
DownloadScheduler = null;
}
}

View File

@@ -9,7 +9,7 @@ namespace YooAsset
CheckAppFootPrint,
SearchCacheFiles,
VerifyCacheFiles,
CreateDownloadCenter,
CreateDownloadScheduler,
Done,
}
@@ -110,7 +110,7 @@ namespace YooAsset
if (_verifyCacheFilesOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CreateDownloadCenter;
_steps = ESteps.CreateDownloadScheduler;
YooLogger.Log($"Package '{_fileSystem.PackageName}' '{_fileSystem.GetType().Name}' cached files count : {_fileSystem.FileCount}");
}
else
@@ -121,13 +121,13 @@ namespace YooAsset
}
}
if (_steps == ESteps.CreateDownloadCenter)
if (_steps == ESteps.CreateDownloadScheduler)
{
// 注意:下载中心作为独立任务运行!
if (_fileSystem.DownloadCenter == null)
if (_fileSystem.DownloadScheduler == null)
{
_fileSystem.DownloadCenter = new DownloadCenterOperation(_fileSystem);
OperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.DownloadCenter);
_fileSystem.DownloadScheduler = new DownloadSchedulerOperation(_fileSystem);
OperationSystem.StartOperation(_fileSystem.PackageName, _fileSystem.DownloadScheduler);
}
_steps = ESteps.Done;

View File

@@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
namespace YooAsset
{
internal class DownloadCenterOperation : AsyncOperationBase
{
private readonly DefaultCacheFileSystem _fileSystem;
protected readonly Dictionary<string, UnityDownloadFileOperation> _downloaders = new Dictionary<string, UnityDownloadFileOperation>(1000);
protected readonly List<string> _removeList = new List<string>(1000);
public DownloadCenterOperation(DefaultCacheFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
}
internal override void InternalUpdate()
{
// 获取可移除的下载器集合
_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))
{
Childs.Remove(downloader);
_downloaders.Remove(key);
}
}
// 最大并发数检测
int processCount = GetProcessingOperationCount();
if (processCount != _downloaders.Count)
{
if (processCount < _fileSystem.DownloadMaxConcurrency)
{
int startCount = _fileSystem.DownloadMaxConcurrency - processCount;
if (startCount > _fileSystem.DownloadMaxRequestPerFrame)
startCount = _fileSystem.DownloadMaxRequestPerFrame;
foreach (var operationPair in _downloaders)
{
var operation = operationPair.Value;
if (operation.Status == EOperationStatus.None)
{
operation.StartOperation();
startCount--;
if (startCount <= 0)
break;
}
}
}
}
}
/// <summary>
/// 创建下载任务
/// </summary>
public UnityDownloadFileOperation DownloadFileAsync(PackageBundle bundle, string url)
{
// 查询旧的下载器
if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader))
{
oldDownloader.Reference();
return oldDownloader;
}
// 创建新的下载器
UnityDownloadFileOperation newDownloader;
bool isRequestLocalFile = DownloadSystemHelper.IsRequestLocalFile(url);
if (isRequestLocalFile)
{
newDownloader = new UnityDownloadLocalFileOperation(_fileSystem, bundle, url);
AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader);
}
else
{
if (bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize)
{
newDownloader = new UnityDownloadResumeFileOperation(_fileSystem, bundle, url);
AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader);
}
else
{
newDownloader = new UnityDownloadNormalFileOperation(_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: 768ef2df3433df245a26fec28d022e45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using System.IO;
using UnityEngine;
namespace YooAsset
{
@@ -17,7 +18,7 @@ namespace YooAsset
// 下载参数
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly DownloadFileOptions _options;
private UnityDownloadFileOperation _unityDownloadFileOp;
private DownloadAndCacheFileOperation _downloadFileOp;
protected int _requestCount = 0;
protected float _tryAgainTimer = 0;
@@ -67,7 +68,7 @@ namespace YooAsset
}
string url = GetRequestURL();
_unityDownloadFileOp = _fileSystem.DownloadCenter.DownloadFileAsync(Bundle, url);
_downloadFileOp = _fileSystem.DownloadScheduler.DownloadAndCacheFileAsync(Bundle, url);
_steps = ESteps.CheckRequest;
}
@@ -75,20 +76,16 @@ namespace YooAsset
if (_steps == ESteps.CheckRequest)
{
if (IsWaitForAsyncComplete)
_unityDownloadFileOp.WaitForAsyncComplete();
_downloadFileOp.WaitForAsyncComplete();
// 因为并发数量限制,下载器可能被挂起!
if (_unityDownloadFileOp.Status == EOperationStatus.None)
_downloadFileOp.UpdateOperation();
Progress = _downloadFileOp.Progress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
DownloadProgress = _downloadFileOp.DownloadProgress;
if (_downloadFileOp.IsDone == false)
return;
_unityDownloadFileOp.UpdateOperation();
Progress = _unityDownloadFileOp.Progress;
DownloadedBytes = _unityDownloadFileOp.DownloadedBytes;
DownloadProgress = _unityDownloadFileOp.DownloadProgress;
if (_unityDownloadFileOp.IsDone == false)
return;
if (_unityDownloadFileOp.Status == EOperationStatus.Succeed)
if (_downloadFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
@@ -98,13 +95,13 @@ namespace YooAsset
if (IsWaitForAsyncComplete == false && _failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_unityDownloadFileOp.URL} Try again !");
YooLogger.Warning($"Failed download : {_downloadFileOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _unityDownloadFileOp.Error;
Error = _downloadFileOp.Error;
YooLogger.Error(Error);
}
}
@@ -141,9 +138,9 @@ namespace YooAsset
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_unityDownloadFileOp != null)
if (_downloadFileOp != null)
{
_unityDownloadFileOp.Release();
_downloadFileOp.Release();
}
}
}

View File

@@ -15,7 +15,7 @@ namespace YooAsset
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebFileRequestOperation _webFileRequestOp;
private IDownloadFileRequest _webFileRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -57,16 +57,16 @@ namespace YooAsset
string savePath = _fileSystem.GetCachePackageHashFilePath(_packageVersion);
string fileName = YooAssetSettingsData.GetPackageHashFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetWebRequestURL(fileName);
_webFileRequestOp = new UnityWebFileRequestOperation(webURL, savePath, _timeout);
_webFileRequestOp.StartOperation();
AddChildOperation(_webFileRequestOp);
int watchdogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadFileRequestArgs(webURL, savePath, _timeout, watchdogTime);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
_webFileRequestOp.UpdateOperation();
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EOperationStatus.Succeed)
if (_webFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@@ -15,7 +15,7 @@ namespace YooAsset
private readonly DefaultCacheFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebFileRequestOperation _webFileRequestOp;
private IDownloadFileRequest _webFileRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -57,16 +57,16 @@ namespace YooAsset
string savePath = _fileSystem.GetCachePackageManifestFilePath(_packageVersion);
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_fileSystem.PackageName, _packageVersion);
string webURL = GetDownloadRequestURL(fileName);
_webFileRequestOp = new UnityWebFileRequestOperation(webURL, savePath, _timeout);
_webFileRequestOp.StartOperation();
AddChildOperation(_webFileRequestOp);
int watchdogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadFileRequestArgs(webURL, savePath, _timeout, watchdogTime);
_webFileRequestOp = _fileSystem.DownloadBackend.CreateFileRequest(args);
_webFileRequestOp.SendRequest();
}
_webFileRequestOp.UpdateOperation();
if (_webFileRequestOp.IsDone == false)
return;
if (_webFileRequestOp.Status == EOperationStatus.Succeed)
if (_webFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@@ -13,7 +13,7 @@ namespace YooAsset
private readonly DefaultCacheFileSystem _fileSystem;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -45,17 +45,17 @@ namespace YooAsset
{
string fileName = YooAssetSettingsData.GetPackageVersionFileName(_fileSystem.PackageName);
string url = GetWebRequestURL(fileName);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
int watchDogTime = _fileSystem.DownloadWatchDogTime;
var args = new DownloadDataRequestArgs(url, _timeout, watchDogTime);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
Progress = _webTextRequestOp.Progress;
Progress = _webTextRequestOp.DownloadProgress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageVersion))

View File

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

View File

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

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

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

View File

@@ -1,94 +1,79 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class UnityDownloadLocalFileOperation : UnityDownloadFileOperation
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 UnityDownloadLocalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url)
: base(fileSystem, bundle, url)
internal DownloadAndCacheLocalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url) : base(url)
{
_fileSystem = fileSystem;
_bundle = bundle;
_tempFilePath = _fileSystem.GetTempFilePath(_bundle);
}
internal override void InternalStart()
{
if (_fileSystem.CopyLocalFileServices != null)
_steps = ESteps.CopyLocalFile;
else
_steps = ESteps.CreateRequest;
_steps = ESteps.CheckCopy;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载器
if (_steps == ESteps.CreateRequest)
// 检测文件拷贝
if (_steps == ESteps.CheckCopy)
{
// 删除历史缓存文件
FileUtility.CreateFileDirectory(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
CreateWebRequest();
_steps = ESteps.Download;
}
// 检测下载结果
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
UpdateWatchDog();
if (_webRequest.isDone == false)
return;
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.VerifyFile;
}
if (_fileSystem.CopyLocalFileServices != null)
_steps = ESteps.CopyLocalFile;
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
_steps = ESteps.CreateRequest;
}
// 拷贝内置文件
// 拷贝本地文件
if (_steps == ESteps.CopyLocalFile)
{
FileUtility.CreateFileDirectory(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
try
{
//TODO 团结引擎,在某些机型(红米),拷贝包内文件会小概率失败!需要借助其它方式来拷贝包内文件。
var localFileInfo = new LocalFileInfo();
localFileInfo.PackageName = _fileSystem.PackageName;
localFileInfo.BundleName = _bundle.BundleName;
localFileInfo.SourceFileURL = _requestURL;
localFileInfo.SourceFileURL = URL;
_fileSystem.CopyLocalFileServices.CopyFile(localFileInfo, _tempFilePath);
if (File.Exists(_tempFilePath))
{
DownloadProgress = 1f;
DownloadedBytes = _bundle.FileSize;
Progress = DownloadProgress;
_steps = ESteps.VerifyFile;
_steps = ESteps.VerifyBundleFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed copy local file : {_requestURL}";
Error = $"Failed copy local file : {URL}";
}
}
catch (System.Exception ex)
@@ -99,8 +84,44 @@ namespace YooAsset
}
}
// 验证下载文件
if (_steps == ESteps.VerifyFile)
// 创建下载请求
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)
{
@@ -119,35 +140,51 @@ namespace YooAsset
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
if (_fileSystem.WriteCacheBundleFile(_bundle, _tempFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
}
_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();
InternalUpdate();
if (IsDone)
break;
@@ -156,15 +193,5 @@ namespace YooAsset
System.Threading.Thread.Sleep(1);
}
}
private void CreateWebRequest()
{
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -0,0 +1,206 @@
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

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

View File

@@ -0,0 +1,185 @@
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;
}
}
}
}
}
/// <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

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

View File

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

namespace YooAsset
{
internal abstract class UnityDownloadFileOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
CopyLocalFile,
VerifyFile,
Done,
}
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly PackageBundle _bundle;
protected readonly string _tempFilePath;
private bool _watchDogInit = false;
private bool _watchDogAborted = false;
private ulong _lastDownloadBytes;
private double _lastGetDataTime;
/// <summary>
/// 引用计数
/// </summary>
public int RefCount { private set; get; }
internal UnityDownloadFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url) : base(url)
{
_fileSystem = fileSystem;
_bundle = bundle;
_tempFilePath = _fileSystem.GetTempFilePath(bundle);
}
internal override string InternalGetDesc()
{
return $"RefCount : {RefCount}";
}
/// <summary>
/// 更新看门狗监测
/// 说明:监控时间范围内,如果没有接收到任何下载数据,那么直接终止任务!
/// </summary>
protected void UpdateWatchDog()
{
if (_fileSystem.DownloadWatchDogTime == int.MaxValue)
return;
if (_watchDogAborted)
return;
#if UNITY_2020_3_OR_NEWER
double realtimeSinceStartup = UnityEngine.Time.realtimeSinceStartupAsDouble;
#else
double realtimeSinceStartup = UnityEngine.Time.realtimeSinceStartup;
#endif
if (_watchDogInit == false)
{
_watchDogInit = true;
_lastDownloadBytes = 0;
_lastGetDataTime = realtimeSinceStartup;
}
if (_webRequest.downloadedBytes != _lastDownloadBytes)
{
_lastDownloadBytes = _webRequest.downloadedBytes;
_lastGetDataTime = realtimeSinceStartup;
}
else
{
double deltaTime = realtimeSinceStartup - _lastGetDataTime;
if (deltaTime > _fileSystem.DownloadWatchDogTime)
{
_watchDogAborted = true;
InternalAbort(); //终止网络请求
}
}
}
/// <summary>
/// 减少引用计数
/// </summary>
public void Release()
{
RefCount--;
}
/// <summary>
/// 增加引用计数
/// </summary>
public void Reference()
{
RefCount++;
}
}
}

View File

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

View File

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

View File

@@ -1,124 +0,0 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class UnityDownloadNormalFileOperation : UnityDownloadFileOperation
{
private VerifyTempFileOperation _verifyOperation;
private ESteps _steps = ESteps.None;
internal UnityDownloadNormalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url)
: base(fileSystem, bundle, url)
{
}
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);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
CreateWebRequest();
_steps = ESteps.Download;
}
// 检测下载结果
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
UpdateWatchDog();
if (_webRequest.isDone == false)
return;
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.VerifyFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyFile)
{
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)
{
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 ! {_tempFilePath}";
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote : {_requestURL} !");
}
}
private void CreateWebRequest()
{
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -1,158 +0,0 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class UnityDownloadResumeFileOperation : UnityDownloadFileOperation
{
private VerifyTempFileOperation _verifyOperation;
private long _fileOriginLength = 0;
private ESteps _steps = ESteps.None;
internal UnityDownloadResumeFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url)
: base(fileSystem, bundle, url)
{
}
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);
// 获取下载起始位置
_fileOriginLength = 0;
long fileBeginLength = -1;
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= _bundle.FileSize)
{
File.Delete(_tempFilePath);
}
else
{
fileBeginLength = fileInfo.Length;
_fileOriginLength = fileBeginLength;
DownloadedBytes = _fileOriginLength;
}
}
CreateWebRequest(fileBeginLength);
_steps = ESteps.Download;
}
// 检测下载结果
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _fileOriginLength + (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
UpdateWatchDog();
if (_webRequest.isDone == false)
return;
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.VerifyFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 在遇到特殊错误的时候删除文件
ClearTempFileWhenError();
// 注意:最终释放请求器
DisposeRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyFile)
{
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)
{
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 !";
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote : {_requestURL} !");
}
}
private void ClearTempFileWhenError()
{
if (_fileSystem.ResumeDownloadResponseCodes == null)
return;
//说明:如果遇到以下错误返回码,验证失败直接删除文件
if (_fileSystem.ResumeDownloadResponseCodes.Contains(HttpCode))
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
private void CreateWebRequest(long fileBeginLength)
{
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
if (fileBeginLength > 0)
_webRequest.SetRequestHeader("Range", $"bytes={fileBeginLength}-");
_webRequest.SendWebRequest();
}
}
}

View File

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

View File

@@ -11,6 +11,11 @@ namespace YooAsset
protected readonly Dictionary<string, string> _records = new Dictionary<string, string>(10000);
protected string _packageRoot;
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
@@ -145,6 +150,10 @@ namespace YooAsset
throw new YooFileSystemException($"{nameof(DefaultEditorFileSystem)} package root is null or empty !");
_packageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(DownloadSystemHelper.UnityWebRequestCreater);
}
public virtual void OnDestroy()
{

View File

@@ -17,7 +17,7 @@ namespace YooAsset
// 下载参数
protected readonly DefaultEditorFileSystem _fileSystem;
protected readonly DownloadFileOptions _options;
protected UnityVirtualBundleRequestOperation _unityDownloadFileOp;
protected IDownloadFileRequest _downloadFileOp;
protected int _requestCount = 0;
protected float _tryAgainTimer = 0;
@@ -68,29 +68,22 @@ namespace YooAsset
string url = GetRequestURL();
int speed = _fileSystem.VirtualDownloadSpeed;
_unityDownloadFileOp = new UnityVirtualBundleRequestOperation(Bundle, speed, url);
_unityDownloadFileOp.StartOperation();
var args = new DownloadSimulateRequestArgs(url, Bundle.FileSize, speed);
_downloadFileOp = _fileSystem.DownloadBackend.CreateSimulateRequest(args);
_downloadFileOp.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
if (IsWaitForAsyncComplete)
_unityDownloadFileOp.WaitForAsyncComplete();
// 因为并发数量限制,下载器可能被挂起!
if (_unityDownloadFileOp.Status == EOperationStatus.None)
Progress = _downloadFileOp.DownloadProgress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
DownloadProgress = _downloadFileOp.DownloadProgress;
if (_downloadFileOp.IsDone == false)
return;
_unityDownloadFileOp.UpdateOperation();
Progress = _unityDownloadFileOp.Progress;
DownloadedBytes = _unityDownloadFileOp.DownloadedBytes;
DownloadProgress = _unityDownloadFileOp.DownloadProgress;
if (_unityDownloadFileOp.IsDone == false)
return;
if (_unityDownloadFileOp.Status == EOperationStatus.Succeed)
if (_downloadFileOp.Status == EDownloadRequestStatus.Succeed)
{
_fileSystem.RecordDownloadFile(Bundle);
_steps = ESteps.Done;
@@ -101,13 +94,13 @@ namespace YooAsset
if (IsWaitForAsyncComplete == false && _failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_unityDownloadFileOp.URL} Try again !");
YooLogger.Warning($"Failed download : {_downloadFileOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _unityDownloadFileOp.Error;
Error = _downloadFileOp.Error;
YooLogger.Error(Error);
}
}
@@ -130,13 +123,11 @@ namespace YooAsset
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
if (_steps != ESteps.Done)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Try load bundle {Bundle.BundleName} from remote !";
}
}

View File

@@ -10,6 +10,11 @@ namespace YooAsset
/// </summary>
internal class DefaultWebRemoteFileSystem : IFileSystem
{
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
@@ -128,6 +133,10 @@ namespace YooAsset
public virtual void OnCreate(string packageName, string packageRoot)
{
PackageName = packageName;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(DownloadSystemHelper.UnityWebRequestCreater);
}
public virtual void OnDestroy()
{

View File

@@ -38,16 +38,16 @@ namespace YooAsset
string fallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue);
options.SetURL(mainURL, fallbackURL);
if (_bundle.Encrypted)
{
_loadWebAssetBundleOp = new LoadWebEncryptAssetBundleOperation(_bundle, options, _fileSystem.DecryptionServices);
_loadWebAssetBundleOp = new LoadWebEncryptAssetBundleOperation(_bundle, options, _fileSystem.DecryptionServices, _fileSystem.DownloadBackend);
_loadWebAssetBundleOp.StartOperation();
AddChildOperation(_loadWebAssetBundleOp);
}
else
{
_loadWebAssetBundleOp = new LoadWebNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache);
_loadWebAssetBundleOp = new LoadWebNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache, _fileSystem.DownloadBackend);
_loadWebAssetBundleOp.StartOperation();
AddChildOperation(_loadWebAssetBundleOp);
}

View File

@@ -38,7 +38,7 @@ namespace YooAsset
{
if (_requestWebPackageHashOp == null)
{
_requestWebPackageHashOp = new RequestWebPackageHashOperation(_fileSystem.RemoteServices, _fileSystem.PackageName, _packageVersion, _timeout);
_requestWebPackageHashOp = new RequestWebPackageHashOperation(_fileSystem.RemoteServices, _fileSystem.DownloadBackend, _fileSystem.PackageName, _packageVersion, _timeout);
_requestWebPackageHashOp.StartOperation();
AddChildOperation(_requestWebPackageHashOp);
}
@@ -67,7 +67,8 @@ namespace YooAsset
string packageName = _fileSystem.PackageName;
var manifestServices = _fileSystem.ManifestServices;
var remoteServices = _fileSystem.RemoteServices;
_loadWebPackageManifestOp = new LoadWebPackageManifestOperation(manifestServices, remoteServices, packageName, _packageVersion, packageHash, _timeout);
var downloadBackend = _fileSystem.DownloadBackend;
_loadWebPackageManifestOp = new LoadWebPackageManifestOperation(manifestServices, remoteServices, downloadBackend, packageName, _packageVersion, packageHash, _timeout);
_loadWebPackageManifestOp.StartOperation();
AddChildOperation(_loadWebPackageManifestOp);
}

View File

@@ -36,7 +36,7 @@ namespace YooAsset
{
if (_requestWebPackageVersionOp == null)
{
_requestWebPackageVersionOp = new RequestWebPackageVersionOperation(_fileSystem.RemoteServices, _fileSystem.PackageName, _appendTimeTicks, _timeout);
_requestWebPackageVersionOp = new RequestWebPackageVersionOperation(_fileSystem.RemoteServices, _fileSystem.DownloadBackend, _fileSystem.PackageName, _appendTimeTicks, _timeout);
_requestWebPackageVersionOp.StartOperation();
AddChildOperation(_requestWebPackageVersionOp);
}

View File

@@ -24,6 +24,11 @@ namespace YooAsset
protected readonly Dictionary<string, string> _webFilePathMapping = new Dictionary<string, string>(10000);
protected string _webPackageRoot = string.Empty;
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { private set; get; }
/// <summary>
/// 包裹名称
/// </summary>
@@ -138,6 +143,10 @@ namespace YooAsset
_webPackageRoot = GetDefaultWebPackageRoot(packageName);
else
_webPackageRoot = packageRoot;
// 创建默认的下载后台接口
if (DownloadBackend == null)
DownloadBackend = new UnityWebRequestBackend(DownloadSystemHelper.UnityWebRequestCreater);
}
public virtual void OnDestroy()
{

View File

@@ -41,13 +41,13 @@ namespace YooAsset
if (_bundle.Encrypted)
{
_loadWebAssetBundleOp = new LoadWebEncryptAssetBundleOperation(_bundle, options, _fileSystem.DecryptionServices);
_loadWebAssetBundleOp = new LoadWebEncryptAssetBundleOperation(_bundle, options, _fileSystem.DecryptionServices, _fileSystem.DownloadBackend);
_loadWebAssetBundleOp.StartOperation();
AddChildOperation(_loadWebAssetBundleOp);
}
else
{
_loadWebAssetBundleOp = new LoadWebNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache);
_loadWebAssetBundleOp = new LoadWebNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache, _fileSystem.DownloadBackend);
_loadWebAssetBundleOp.StartOperation();
AddChildOperation(_loadWebAssetBundleOp);
}

View File

@@ -14,7 +14,7 @@ namespace YooAsset
private readonly DefaultWebServerFileSystem _fileSystem;
private readonly int _timeout;
private UnityWebDataRequestOperation _webDataRequestOp;
private IDownloadBytesRequest _webDataRequestOp;
private ESteps _steps = ESteps.None;
internal LoadWebServerCatalogFileOperation(DefaultWebServerFileSystem fileSystem, int timeout)
@@ -37,16 +37,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetCatalogBinaryFileLoadPath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webDataRequestOp = new UnityWebDataRequestOperation(url, _timeout);
_webDataRequestOp.StartOperation();
AddChildOperation(_webDataRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
_webDataRequestOp.UpdateOperation();
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EOperationStatus.Succeed)
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.LoadCatalog;
}

View File

@@ -16,7 +16,7 @@ namespace YooAsset
private readonly string _packageVersion;
private readonly string _packageHash;
private readonly int _timeout;
private UnityWebDataRequestOperation _webDataRequestOp;
private IDownloadBytesRequest _webDataRequestOp;
private DeserializeManifestOperation _deserializer;
private ESteps _steps = ESteps.None;
@@ -48,16 +48,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetWebPackageManifestFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webDataRequestOp = new UnityWebDataRequestOperation(url, _timeout);
_webDataRequestOp.StartOperation();
AddChildOperation(_webDataRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webDataRequestOp = _fileSystem.DownloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
_webDataRequestOp.UpdateOperation();
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EOperationStatus.Succeed)
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.VerifyFileData;
}

View File

@@ -13,7 +13,7 @@ namespace YooAsset
private readonly DefaultWebServerFileSystem _fileSystem;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
@@ -43,17 +43,16 @@ namespace YooAsset
{
string filePath = _fileSystem.GetWebPackageHashFilePath(_packageVersion);
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
Progress = _webTextRequestOp.Progress;
Progress = _webTextRequestOp.DownloadProgress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageHash = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageHash))

View File

@@ -12,7 +12,7 @@ namespace YooAsset
private readonly DefaultWebServerFileSystem _fileSystem;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
@@ -41,16 +41,15 @@ namespace YooAsset
{
string filePath = _fileSystem.GetWebPackageVersionFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webTextRequestOp = _fileSystem.DownloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageVersion))

View File

@@ -16,19 +16,21 @@ namespace YooAsset
private readonly PackageBundle _bundle;
private readonly DownloadFileOptions _options;
private readonly IWebDecryptionServices _decryptionServices;
private UnityWebDataRequestOperation _unityWebDataRequestOp;
private readonly IDownloadBackend _downloadBackend;
private IDownloadBytesRequest _unityWebDataRequestOp;
private int _requestCount = 0;
private float _tryAgainTimer = 0;
private int _failedTryAgain;
private ESteps _steps = ESteps.None;
internal LoadWebEncryptAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, IWebDecryptionServices decryptionServices)
internal LoadWebEncryptAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, IWebDecryptionServices decryptionServices, IDownloadBackend downloadBackend)
{
_bundle = bundle;
_options = options;
_failedTryAgain = options.FailedTryAgain;
_decryptionServices = decryptionServices;
_downloadBackend = downloadBackend;
}
internal override void InternalStart()
{
@@ -52,24 +54,23 @@ namespace YooAsset
}
string url = GetRequestURL();
_unityWebDataRequestOp = new UnityWebDataRequestOperation(url, 0);
_unityWebDataRequestOp.StartOperation();
AddChildOperation(_unityWebDataRequestOp);
var args = new DownloadDataRequestArgs(url, 0, 0);
_unityWebDataRequestOp = _downloadBackend.CreateBytesRequest(args);
_unityWebDataRequestOp.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
_unityWebDataRequestOp.UpdateOperation();
Progress = _unityWebDataRequestOp.Progress;
Progress = _unityWebDataRequestOp.DownloadProgress;
DownloadProgress = _unityWebDataRequestOp.DownloadProgress;
DownloadedBytes = _unityWebDataRequestOp.DownloadedBytes;
if (_unityWebDataRequestOp.IsDone == false)
return;
// 检查网络错误
if (_unityWebDataRequestOp.Status == EOperationStatus.Succeed)
if (_unityWebDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
AssetBundle assetBundle = LoadEncryptedAssetBundle(_unityWebDataRequestOp.Result);
if (assetBundle == null)

View File

@@ -16,7 +16,8 @@ namespace YooAsset
private readonly PackageBundle _bundle;
private readonly DownloadFileOptions _options;
private readonly bool _disableUnityWebCache;
private UnityAssetBundleRequestOperation _unityAssetBundleRequestOp;
private readonly IDownloadBackend _downloadBackend;
private IDownloadAssetBundleRequest _unityAssetBundleRequestOp;
private int _requestCount = 0;
private float _tryAgainTimer = 0;
@@ -24,12 +25,13 @@ namespace YooAsset
private ESteps _steps = ESteps.None;
internal LoadWebNormalAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, bool disableUnityWebCache)
internal LoadWebNormalAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, bool disableUnityWebCache, IDownloadBackend downloadBackend)
{
_bundle = bundle;
_options = options;
_failedTryAgain = options.FailedTryAgain;
_disableUnityWebCache = disableUnityWebCache;
_downloadBackend = downloadBackend;
}
internal override void InternalStart()
{
@@ -44,23 +46,22 @@ namespace YooAsset
if (_steps == ESteps.CreateRequest)
{
string url = GetRequestURL();
_unityAssetBundleRequestOp = new UnityAssetBundleRequestOperation(_bundle, _disableUnityWebCache, url);
_unityAssetBundleRequestOp.StartOperation();
AddChildOperation(_unityAssetBundleRequestOp);
var args = new DownloadAssetBundleRequestArgs(url, 0, 0, _disableUnityWebCache, _bundle.FileHash, _bundle.UnityCRC);
_unityAssetBundleRequestOp = _downloadBackend.CreateAssetBundleRequest(args);
_unityAssetBundleRequestOp.SendRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
_unityAssetBundleRequestOp.UpdateOperation();
Progress = _unityAssetBundleRequestOp.Progress;
Progress = _unityAssetBundleRequestOp.DownloadProgress;
DownloadedBytes = _unityAssetBundleRequestOp.DownloadedBytes;
DownloadProgress = _unityAssetBundleRequestOp.DownloadProgress;
if (_unityAssetBundleRequestOp.IsDone == false)
return;
if (_unityAssetBundleRequestOp.Status == EOperationStatus.Succeed)
if (_unityAssetBundleRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@@ -13,11 +13,12 @@ internal class LoadWebPackageManifestOperation : AsyncOperationBase
private readonly IManifestRestoreServices _manifestServices;
private readonly IRemoteServices _remoteServices;
private readonly IDownloadBackend _downloadBackend;
private readonly string _packageName;
private readonly string _packageVersion;
private readonly string _packageHash;
private readonly int _timeout;
private UnityWebDataRequestOperation _webDataRequestOp;
private IDownloadBytesRequest _webDataRequestOp;
private DeserializeManifestOperation _deserializer;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -28,11 +29,12 @@ internal class LoadWebPackageManifestOperation : AsyncOperationBase
public PackageManifest Manifest { private set; get; }
internal LoadWebPackageManifestOperation(IManifestRestoreServices manifestServices, IRemoteServices remoteServices,
internal LoadWebPackageManifestOperation(IManifestRestoreServices manifestServices, IRemoteServices remoteServices, IDownloadBackend downloadBackend,
string packageName, string packageVersion, string packageHash, int timeout)
{
_manifestServices = manifestServices;
_remoteServices = remoteServices;
_downloadBackend = downloadBackend;
_packageName = packageName;
_packageVersion = packageVersion;
_packageHash = packageHash;
@@ -54,17 +56,16 @@ internal class LoadWebPackageManifestOperation : AsyncOperationBase
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_packageName, _packageVersion);
string url = GetRequestURL(fileName);
_webDataRequestOp = new UnityWebDataRequestOperation(url, _timeout);
_webDataRequestOp.StartOperation();
AddChildOperation(_webDataRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webDataRequestOp = _downloadBackend.CreateBytesRequest(args);
_webDataRequestOp.SendRequest();
}
_webDataRequestOp.UpdateOperation();
Progress = _webDataRequestOp.Progress;
Progress = _webDataRequestOp.DownloadProgress;
if (_webDataRequestOp.IsDone == false)
return;
if (_webDataRequestOp.Status == EOperationStatus.Succeed)
if (_webDataRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.VerifyFileData;
}

View File

@@ -10,10 +10,11 @@ internal class RequestWebPackageHashOperation : AsyncOperationBase
}
private readonly IRemoteServices _remoteServices;
private readonly IDownloadBackend _downloadBackend;
private readonly string _packageName;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -23,9 +24,10 @@ internal class RequestWebPackageHashOperation : AsyncOperationBase
public string PackageHash { private set; get; }
public RequestWebPackageHashOperation(IRemoteServices remoteServices, string packageName, string packageVersion, int timeout)
public RequestWebPackageHashOperation(IRemoteServices remoteServices, IDownloadBackend downloadBackend, string packageName, string packageVersion, int timeout)
{
_remoteServices = remoteServices;
_downloadBackend = downloadBackend;
_packageName = packageName;
_packageVersion = packageVersion;
_timeout = timeout;
@@ -46,17 +48,16 @@ internal class RequestWebPackageHashOperation : AsyncOperationBase
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(_packageName, _packageVersion);
string url = GetRequestURL(fileName);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webTextRequestOp = _downloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
Progress = _webTextRequestOp.Progress;
Progress = _webTextRequestOp.DownloadProgress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageHash = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageHash))

View File

@@ -10,10 +10,11 @@ internal class RequestWebPackageVersionOperation : AsyncOperationBase
}
private readonly IRemoteServices _remoteServices;
private readonly IDownloadBackend _downloadBackend;
private readonly string _packageName;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private UnityWebTextRequestOperation _webTextRequestOp;
private IDownloadTextRequest _webTextRequestOp;
private int _requestCount = 0;
private ESteps _steps = ESteps.None;
@@ -23,9 +24,10 @@ internal class RequestWebPackageVersionOperation : AsyncOperationBase
public string PackageVersion { private set; get; }
public RequestWebPackageVersionOperation(IRemoteServices remoteServices, string packageName, bool appendTimeTicks, int timeout)
public RequestWebPackageVersionOperation(IRemoteServices remoteServices, IDownloadBackend downloadBackend, string packageName, bool appendTimeTicks, int timeout)
{
_remoteServices = remoteServices;
_downloadBackend = downloadBackend;
_packageName = packageName;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
@@ -46,17 +48,16 @@ internal class RequestWebPackageVersionOperation : AsyncOperationBase
{
string fileName = YooAssetSettingsData.GetPackageVersionFileName(_packageName);
string url = GetRequestURL(fileName);
_webTextRequestOp = new UnityWebTextRequestOperation(url, _timeout);
_webTextRequestOp.StartOperation();
AddChildOperation(_webTextRequestOp);
var args = new DownloadDataRequestArgs(url, _timeout, 0);
_webTextRequestOp = _downloadBackend.CreateTextRequest(args);
_webTextRequestOp.SendRequest();
}
_webTextRequestOp.UpdateOperation();
Progress = _webTextRequestOp.Progress;
Progress = _webTextRequestOp.DownloadProgress;
if (_webTextRequestOp.IsDone == false)
return;
if (_webTextRequestOp.Status == EOperationStatus.Succeed)
if (_webTextRequestOp.Status == EDownloadRequestStatus.Succeed)
{
PackageVersion = _webTextRequestOp.Result;
if (string.IsNullOrEmpty(PackageVersion))

View File

@@ -139,6 +139,19 @@ namespace YooAsset
Childs.Add(child);
}
/// <summary>
/// 移除子任务
/// </summary>
internal void RemoveChildOperation(AsyncOperationBase child)
{
#if UNITY_EDITOR
if (Childs.Contains(child) == false)
throw new YooInternalException($"The child node {child.GetType().Name} not exists !");
#endif
Childs.Remove(child);
}
/// <summary>
/// 获取异步操作说明
/// </summary>

View File

@@ -21,14 +21,16 @@ public class CopyBuildinManifestOperation : GameAsyncOperation
private readonly string _packageName;
private readonly string _packageVersion;
private readonly IDownloadBackend _backend;
private IDownloadFileRequest _hashFileRequestOp;
private IDownloadFileRequest _manifestFileRequestOp;
private ESteps _steps = ESteps.None;
private UnityWebFileRequestOperation _hashFileRequestOp;
private UnityWebFileRequestOperation _manifestFileRequestOp;
public CopyBuildinManifestOperation(string packageName, string packageVersion)
{
_packageName = packageName;
_packageVersion = packageVersion;
_backend = new UnityWebRequestBackend();
}
protected override void OnStart()
{
@@ -58,14 +60,15 @@ public class CopyBuildinManifestOperation : GameAsyncOperation
string sourcePath = GetBuildinHashFilePath();
string destPath = GetCacheHashFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(sourcePath);
_hashFileRequestOp = new UnityWebFileRequestOperation(url, destPath, 60);
OperationSystem.StartOperation(_packageName, _hashFileRequestOp);
var args = new DownloadFileRequestArgs(url, destPath, 60, 0);
_hashFileRequestOp = _backend.CreateFileRequest(args);
_hashFileRequestOp.SendRequest();
}
if (_hashFileRequestOp.IsDone == false)
return;
if (_hashFileRequestOp.Status == EOperationStatus.Succeed)
if (_hashFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.CheckManifestFile;
}
@@ -97,14 +100,15 @@ public class CopyBuildinManifestOperation : GameAsyncOperation
string sourcePath = GetBuildinManifestFilePath();
string destPath = GetCacheManifestFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(sourcePath);
_manifestFileRequestOp = new UnityWebFileRequestOperation(url, destPath, 60);
OperationSystem.StartOperation(_packageName, _manifestFileRequestOp);
var args = new DownloadFileRequestArgs(url, destPath, 60, 0);
_manifestFileRequestOp = _backend.CreateFileRequest(args);
_manifestFileRequestOp.SendRequest();
}
if (_manifestFileRequestOp.IsDone == false)
return;
if (_manifestFileRequestOp.Status == EOperationStatus.Succeed)
if (_manifestFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@@ -17,7 +17,8 @@ public class GetBuildinPackageVersionOperation : GameAsyncOperation
}
private readonly string _packageName;
private UnityWebTextRequestOperation _versionFileRequestOp;
private readonly IDownloadBackend _backend;
private IDownloadTextRequest _versionFileRequestOp;
private ESteps _steps = ESteps.None;
/// <summary>
@@ -28,6 +29,7 @@ public class GetBuildinPackageVersionOperation : GameAsyncOperation
public GetBuildinPackageVersionOperation(string packageName)
{
_packageName = packageName;
_backend = new UnityWebRequestBackend();
}
protected override void OnStart()
{
@@ -44,14 +46,15 @@ public class GetBuildinPackageVersionOperation : GameAsyncOperation
{
string filePath = GetBuildinPackageVersionFilePath();
string url = DownloadSystemHelper.ConvertToWWWPath(filePath);
_versionFileRequestOp = new UnityWebTextRequestOperation(url, 60);
OperationSystem.StartOperation(_packageName, _versionFileRequestOp);
var args = new DownloadDataRequestArgs(url, 60, 0);
_versionFileRequestOp = _backend.CreateTextRequest(args);
_versionFileRequestOp.SendRequest();
}
if (_versionFileRequestOp.IsDone == false)
return;
if (_versionFileRequestOp.Status == EOperationStatus.Succeed)
if (_versionFileRequestOp.Status == EDownloadRequestStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;