update runtime code

1. 新增WebGL专属模式WebPlayMode
mlyDevMerge1.5.7
hevinci 2023-07-18 14:27:33 +08:00 committed by QiJing
parent 836b085795
commit 0b748c3163
40 changed files with 1616 additions and 554 deletions

View File

@ -111,7 +111,7 @@ namespace YooAsset
if (loader.CanDestroy()) if (loader.CanDestroy())
{ {
string bundleName = loader.MainBundleInfo.Bundle.BundleName; string bundleName = loader.MainBundleInfo.Bundle.BundleName;
loader.Destroy(false); loader.Destroy();
_loaderList.RemoveAt(i); _loaderList.RemoveAt(i);
_loaderDic.Remove(bundleName); _loaderDic.Remove(bundleName);
} }
@ -123,15 +123,16 @@ namespace YooAsset
/// </summary> /// </summary>
public void ForceUnloadAllAssets() public void ForceUnloadAllAssets()
{ {
#if UNITY_WEBGL
throw new Exception($"WebGL not support invoke {nameof(ForceUnloadAllAssets)}");
#else
foreach (var provider in _providerList) foreach (var provider in _providerList)
{ {
provider.WaitForAsyncComplete(); provider.DestroySafely();
provider.Destroy();
} }
foreach (var loader in _loaderList) foreach (var loader in _loaderList)
{ {
loader.WaitForAsyncComplete(); loader.DestroySafely();
loader.Destroy(true);
} }
_providerList.Clear(); _providerList.Clear();
@ -142,6 +143,7 @@ namespace YooAsset
// 注意:调用底层接口释放所有资源 // 注意:调用底层接口释放所有资源
Resources.UnloadUnusedAssets(); Resources.UnloadUnusedAssets();
#endif
} }
/// <summary> /// <summary>

View File

@ -82,7 +82,8 @@ namespace YooAsset
if (_steps == ESteps.Download) if (_steps == ESteps.Download)
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
_downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain);
_downloader.SendRequest();
_steps = ESteps.CheckDownload; _steps = ESteps.CheckDownload;
} }
@ -112,7 +113,8 @@ namespace YooAsset
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle);
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); _unpacker = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain);
_unpacker.SendRequest();
_steps = ESteps.CheckUnpack; _steps = ESteps.CheckUnpack;
} }
@ -262,9 +264,9 @@ namespace YooAsset
/// <summary> /// <summary>
/// 销毁 /// 销毁
/// </summary> /// </summary>
public override void Destroy(bool forceDestroy) public override void Destroy()
{ {
base.Destroy(forceDestroy); base.Destroy();
if (_stream != null) if (_stream != null)
{ {

View File

@ -15,21 +15,14 @@ namespace YooAsset
private enum ESteps private enum ESteps
{ {
None = 0, None = 0,
Download, LoadWebSiteFile,
CheckDownload, LoadRemoteFile,
LoadCacheFile, CheckLoadFile,
CheckLoadCacheFile,
LoadWebFile,
CheckLoadWebFile,
TryLoadWebFile,
Done, Done,
} }
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
private float _tryTimer = 0; private WebDownloader _downloader;
private DownloaderBase _downloader;
private UnityWebRequest _webRequest;
private AssetBundleCreateRequest _createRequest;
public AssetBundleWebLoader(AssetSystemImpl impl, BundleInfo bundleInfo) : base(impl, bundleInfo) public AssetBundleWebLoader(AssetSystemImpl impl, BundleInfo bundleInfo) : base(impl, bundleInfo)
@ -48,18 +41,13 @@ namespace YooAsset
{ {
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote) if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote)
{ {
_steps = ESteps.Download; _steps = ESteps.LoadRemoteFile;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; FileLoadPath = string.Empty;
} }
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming) else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming)
{ {
_steps = ESteps.LoadWebFile; _steps = ESteps.LoadWebSiteFile;
FileLoadPath = MainBundleInfo.Bundle.StreamingFilePath; FileLoadPath = string.Empty;
}
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
_steps = ESteps.LoadCacheFile;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
} }
else else
{ {
@ -67,143 +55,34 @@ namespace YooAsset
} }
} }
// 1. 从服务器下载 // 1. 跨域获取资源包
if (_steps == ESteps.Download) if (_steps == ESteps.LoadRemoteFile)
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
_downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain) as WebDownloader;
_steps = ESteps.CheckDownload; _downloader.SendRequest(true);
_steps = ESteps.CheckLoadFile;
} }
// 2. 检测服务器下载结果 // 2. 从站点获取资源包
if (_steps == ESteps.CheckDownload) if (_steps == ESteps.LoadWebSiteFile)
{
int failedTryAgain = Impl.DownloadFailedTryAgain;
var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle);
_downloader = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain) as WebDownloader;
_downloader.SendRequest(true);
_steps = ESteps.CheckLoadFile;
}
// 3. 检测加载结果
if (_steps == ESteps.CheckLoadFile)
{ {
DownloadProgress = _downloader.DownloadProgress; DownloadProgress = _downloader.DownloadProgress;
DownloadedBytes = _downloader.DownloadedBytes; DownloadedBytes = _downloader.DownloadedBytes;
if (_downloader.IsDone() == false) if (_downloader.IsDone() == false)
return; return;
if (_downloader.HasError()) CacheBundle = _downloader.GetAssetBundle();
{
_steps = ESteps.Done;
Status = EStatus.Failed;
LastError = _downloader.GetLastError();
}
else
{
_steps = ESteps.LoadCacheFile;
return; //下载完毕等待一帧再去加载!
}
}
// 3. 从本地缓存里加载AssetBundle
if (_steps == ESteps.LoadCacheFile)
{
#if UNITY_EDITOR
// 注意Unity2017.4编辑器模式下如果AssetBundle文件不存在会导致编辑器崩溃这里做了预判。
if (System.IO.File.Exists(FileLoadPath) == false)
{
_steps = ESteps.Done;
Status = EStatus.Failed;
LastError = $"Not found assetBundle file : {FileLoadPath}";
YooLogger.Error(LastError);
return;
}
#endif
// 设置下载进度
DownloadProgress = 1f;
DownloadedBytes = (ulong)MainBundleInfo.Bundle.FileSize;
// Load assetBundle file
var loadMethod = (EBundleLoadMethod)MainBundleInfo.Bundle.LoadMethod;
if (loadMethod == EBundleLoadMethod.Normal)
{
_createRequest = AssetBundle.LoadFromFileAsync(FileLoadPath);
}
else
{
_steps = ESteps.Done;
Status = EStatus.Failed;
LastError = $"WebGL not support encrypted bundle file : {MainBundleInfo.Bundle.BundleName}";
YooLogger.Error(LastError);
return;
}
_steps = ESteps.CheckLoadCacheFile;
}
// 4. 检测AssetBundle加载结果
if (_steps == ESteps.CheckLoadCacheFile)
{
if (_createRequest.isDone == false)
return;
CacheBundle = _createRequest.assetBundle;
if (CacheBundle == null)
{
_steps = ESteps.Done;
Status = EStatus.Failed;
LastError = $"Failed to load AssetBundle file : {MainBundleInfo.Bundle.BundleName}";
YooLogger.Error(LastError);
// 注意当缓存文件的校验等级为Low的时候并不能保证缓存文件的完整性。
// 在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
if (result != EVerifyResult.Succeed)
{
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}");
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
}
}
}
else
{
_steps = ESteps.Done;
Status = EStatus.Succeed;
}
}
// 5. 从WEB网站获取AssetBundle文件
if (_steps == ESteps.LoadWebFile)
{
if (CacheSystem.DisableUnityCacheOnWebGL)
{
_webRequest = UnityWebRequestAssetBundle.GetAssetBundle(FileLoadPath);
}
else
{
var hash = Hash128.Parse(MainBundleInfo.Bundle.FileHash);
_webRequest = UnityWebRequestAssetBundle.GetAssetBundle(FileLoadPath, hash);
}
DownloadSystem.SetUnityWebRequest(_webRequest);
_webRequest.SendWebRequest();
_steps = ESteps.CheckLoadWebFile;
}
// 6. 检测AssetBundle加载结果
if (_steps == ESteps.CheckLoadWebFile)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
return;
#if UNITY_2020_1_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
#endif
{
YooLogger.Warning($"Failed to get asset bundle from web : {FileLoadPath} Error : {_webRequest.error}");
_steps = ESteps.TryLoadWebFile;
_tryTimer = 0;
}
else
{
CacheBundle = DownloadHandlerAssetBundle.GetContent(_webRequest);
if (CacheBundle == null) if (CacheBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
@ -216,19 +95,9 @@ namespace YooAsset
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EStatus.Succeed; Status = EStatus.Succeed;
} }
}
}
// 7. 如果获取失败,重新尝试 // 注意:释放下载句柄
if (_steps == ESteps.TryLoadWebFile) _downloader.DisposeHandler();
{
_tryTimer += Time.unscaledDeltaTime;
if (_tryTimer > 1f)
{
_webRequest.Dispose();
_webRequest = null;
_steps = ESteps.LoadWebFile;
}
} }
} }

View File

@ -144,6 +144,15 @@ namespace YooAsset
} }
} }
/// <summary>
/// 销毁资源包(安全模式)
/// </summary>
public void DestroySafely()
{
WaitForAsyncComplete();
Destroy();
}
/// <summary> /// <summary>
/// 轮询更新 /// 轮询更新
@ -153,18 +162,15 @@ namespace YooAsset
/// <summary> /// <summary>
/// 销毁 /// 销毁
/// </summary> /// </summary>
public virtual void Destroy(bool forceDestroy) public virtual void Destroy()
{ {
IsDestroyed = true; IsDestroyed = true;
// Check fatal // Check fatal
if (forceDestroy == false)
{
if (RefCount > 0) if (RefCount > 0)
throw new Exception($"Bundle file loader ref is not zero : {MainBundleInfo.Bundle.BundleName}"); throw new Exception($"Bundle file loader ref is not zero : {MainBundleInfo.Bundle.BundleName}");
if (IsDone() == false) if (IsDone() == false)
throw new Exception($"Bundle file loader is not done : {MainBundleInfo.Bundle.BundleName}"); throw new Exception($"Bundle file loader is not done : {MainBundleInfo.Bundle.BundleName}");
}
if (CacheBundle != null) if (CacheBundle != null)
{ {

View File

@ -64,7 +64,8 @@ namespace YooAsset
if (_steps == ESteps.Download) if (_steps == ESteps.Download)
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
_downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain);
_downloader.SendRequest();
_steps = ESteps.CheckDownload; _steps = ESteps.CheckDownload;
} }
@ -93,7 +94,8 @@ namespace YooAsset
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle);
_unpacker = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); _unpacker = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain);
_unpacker.SendRequest();
_steps = ESteps.CheckUnpack; _steps = ESteps.CheckUnpack;
} }

View File

@ -47,11 +47,6 @@ namespace YooAsset
_steps = ESteps.Website; _steps = ESteps.Website;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath; FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
} }
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
_steps = ESteps.CheckFile;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else else
{ {
throw new System.NotImplementedException(MainBundleInfo.LoadMode.ToString()); throw new System.NotImplementedException(MainBundleInfo.LoadMode.ToString());
@ -62,7 +57,8 @@ namespace YooAsset
if (_steps == ESteps.Download) if (_steps == ESteps.Download)
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
_downloader = DownloadSystem.BeginDownload(MainBundleInfo, failedTryAgain); _downloader = DownloadSystem.CreateDownload(MainBundleInfo, failedTryAgain);
_downloader.SendRequest();
_steps = ESteps.CheckDownload; _steps = ESteps.CheckDownload;
} }
@ -91,7 +87,8 @@ namespace YooAsset
{ {
int failedTryAgain = Impl.DownloadFailedTryAgain; int failedTryAgain = Impl.DownloadFailedTryAgain;
var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle); var bundleInfo = ManifestTools.ConvertToUnpackInfo(MainBundleInfo.Bundle);
_website = DownloadSystem.BeginDownload(bundleInfo, failedTryAgain); _website = DownloadSystem.CreateDownload(bundleInfo, failedTryAgain);
_website.SendRequest();
_steps = ESteps.CheckWebsite; _steps = ESteps.CheckWebsite;
} }

View File

@ -123,7 +123,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 销毁资源对象 /// 销毁资源对象
/// </summary> /// </summary>
public virtual void Destroy() public void Destroy()
{ {
IsDestroyed = true; IsDestroyed = true;
@ -140,6 +140,22 @@ namespace YooAsset
} }
} }
/// <summary>
/// 销毁资源对象(安全模式)
/// </summary>
public void DestroySafely()
{
if (Status == EStatus.Loading || Status == EStatus.Checking)
{
WaitForAsyncComplete();
Destroy();
}
else
{
Destroy();
}
}
/// <summary> /// <summary>
/// 是否可以销毁 /// 是否可以销毁
/// </summary> /// </summary>

View File

@ -98,34 +98,49 @@ namespace YooAsset
/// <summary> /// <summary>
/// 开始下载资源文件 /// 创建下载器
/// 注意:只有第一次请求的参数才有效 /// 注意:只有第一次请求的参数才有效
/// </summary> /// </summary>
public static DownloaderBase BeginDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60) public static DownloaderBase CreateDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60)
{ {
// 查询存在的下载器 // 查询存在的下载器
if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedDataFilePath, out var downloader)) if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedDataFilePath, out var downloader))
{
return downloader; return downloader;
}
// 如果资源已经缓存 // 如果资源已经缓存
if (CacheSystem.IsCached(bundleInfo.Bundle.PackageName, bundleInfo.Bundle.CacheGUID)) if (CacheSystem.IsCached(bundleInfo.Bundle.PackageName, bundleInfo.Bundle.CacheGUID))
{ {
var tempDownloader = new TempDownloader(bundleInfo); var completedDownloader = new CompletedDownloader(bundleInfo);
return tempDownloader; return completedDownloader;
} }
// 创建新的下载器 // 创建新的下载器
YooLogger.Log($"Beginning to download bundle : {bundleInfo.Bundle.BundleName} URL : {bundleInfo.RemoteMainURL}");
#if UNITY_WEBGL
if (bundleInfo.Bundle.IsRawFile)
{ {
YooLogger.Log($"Beginning to download file : {bundleInfo.Bundle.FileName} URL : {bundleInfo.RemoteMainURL}");
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath); FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath);
bool breakDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize; DownloaderBase newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout);
DownloaderBase newDownloader = new FileDownloader(bundleInfo, breakDownload);
newDownloader.SendRequest(failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader); _downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader; return newDownloader;
} }
else
{
WebDownloader newDownloader = new WebDownloader(bundleInfo, failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
}
#else
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath);
bool resumeDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize;
DownloaderBase newDownloader;
if (resumeDownload)
newDownloader = new FileResumeDownloader(bundleInfo, failedTryAgain, timeout);
else
newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
#endif
} }
/// <summary> /// <summary>
@ -139,14 +154,14 @@ namespace YooAsset
else else
webRequest = new UnityWebRequest(requestURL, UnityWebRequest.kHttpVerbGET); webRequest = new UnityWebRequest(requestURL, UnityWebRequest.kHttpVerbGET);
SetUnityWebRequest(webRequest); SetUnityWebRequestParam(webRequest);
return webRequest; return webRequest;
} }
/// <summary> /// <summary>
/// 设置网络请求的自定义参数 /// 设置网络请求的自定义参数
/// </summary> /// </summary>
public static void SetUnityWebRequest(UnityWebRequest webRequest) private static void SetUnityWebRequestParam(UnityWebRequest webRequest)
{ {
if (RedirectLimit >= 0) if (RedirectLimit >= 0)
webRequest.redirectLimit = RedirectLimit; webRequest.redirectLimit = RedirectLimit;

View File

@ -0,0 +1,23 @@

namespace YooAsset
{
internal sealed class CompletedDownloader : DownloaderBase
{
public CompletedDownloader(BundleInfo bundleInfo) : base(bundleInfo, 0, 0)
{
_downloadProgress = 1f;
_downloadedBytes = (ulong)bundleInfo.Bundle.FileSize;
_status = EStatus.Succeed;
}
public override void SendRequest(params object[] param)
{
}
public override void Update()
{
}
public override void Abort()
{
}
}
}

View File

@ -1,39 +1,41 @@
 using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal abstract class DownloaderBase internal abstract class DownloaderBase
{ {
protected enum ESteps public enum EStatus
{ {
None, None = 0,
CheckTempFile,
WaitingCheckTempFile,
PrepareDownload,
CreateResumeDownloader,
CreateGeneralDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Succeed, Succeed,
Failed, Failed
} }
protected readonly BundleInfo _bundleInfo; protected readonly BundleInfo _bundleInfo;
protected readonly int _timeout;
protected ESteps _steps = ESteps.None;
protected int _timeout;
protected int _failedTryAgain; protected int _failedTryAgain;
protected int _requestCount;
protected string _requestURL;
protected UnityWebRequest _webRequest;
protected EStatus _status = EStatus.None;
protected string _lastError = string.Empty; protected string _lastError = string.Empty;
protected long _lastCode = 0; protected long _lastCode = 0;
// 请求次数
protected int _requestCount = 0;
protected string _requestURL;
// 下载进度
protected float _downloadProgress = 0f; protected float _downloadProgress = 0f;
protected ulong _downloadedBytes = 0; protected ulong _downloadedBytes = 0;
// 超时相关
protected bool _isAbort = false;
protected ulong _latestDownloadBytes;
protected float _latestDownloadRealtime;
protected float _tryAgainTimer;
/// <summary> /// <summary>
/// 是否等待异步结束 /// 是否等待异步结束
/// 警告只能用于解压APP内部资源 /// 警告只能用于解压APP内部资源
@ -57,41 +59,31 @@ namespace YooAsset
} }
public DownloaderBase(BundleInfo bundleInfo) public DownloaderBase(BundleInfo bundleInfo, int failedTryAgain, int timeout)
{ {
_bundleInfo = bundleInfo; _bundleInfo = bundleInfo;
}
public void SendRequest(int failedTryAgain, int timeout)
{
if (_steps == ESteps.None)
{
_failedTryAgain = failedTryAgain; _failedTryAgain = failedTryAgain;
_timeout = timeout; _timeout = timeout;
_steps = ESteps.CheckTempFile;
}
} }
public abstract void SendRequest(params object[] param);
public abstract void Update(); public abstract void Update();
public abstract void Abort(); public abstract void Abort();
/// <summary> /// <summary>
/// 获取网络请求地址 /// 获取下载文件的大小
/// </summary> /// </summary>
protected string GetRequestURL() /// <returns></returns>
public long GetDownloadFileSize()
{ {
// 轮流返回请求地址 return _bundleInfo.Bundle.FileSize;
_requestCount++;
if (_requestCount % 2 == 0)
return _bundleInfo.RemoteFallbackURL;
else
return _bundleInfo.RemoteMainURL;
} }
/// <summary> /// <summary>
/// 获取资源包信息 /// 获取下载文件的资源包名
/// </summary> /// </summary>
public BundleInfo GetBundleInfo() public string GetDownloadBundleName()
{ {
return _bundleInfo; return _bundleInfo.Bundle.BundleName;
} }
/// <summary> /// <summary>
@ -99,7 +91,7 @@ namespace YooAsset
/// </summary> /// </summary>
public bool IsDone() public bool IsDone()
{ {
return _steps == ESteps.Succeed || _steps == ESteps.Failed; return _status == EStatus.Succeed || _status == EStatus.Failed;
} }
/// <summary> /// <summary>
@ -107,7 +99,7 @@ namespace YooAsset
/// </summary> /// </summary>
public bool HasError() public bool HasError()
{ {
return _steps == ESteps.Failed; return _status == EStatus.Failed;
} }
/// <summary> /// <summary>
@ -133,5 +125,69 @@ namespace YooAsset
{ {
return $"Failed to download : {_requestURL} Error : {_lastError} Code : {_lastCode}"; return $"Failed to download : {_requestURL} Error : {_lastError} Code : {_lastCode}";
} }
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _bundleInfo.RemoteFallbackURL;
else
return _bundleInfo.RemoteMainURL;
}
/// <summary>
/// 超时判定方法
/// </summary>
protected void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != DownloadedBytes)
{
_latestDownloadBytes = DownloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
YooLogger.Warning($"Web file request timeout : {_requestURL}");
_webRequest.Abort();
_isAbort = true;
}
}
}
/// <summary>
/// 缓存下载文件
/// </summary>
protected void CachingFile(string tempFilePath)
{
string infoFilePath = _bundleInfo.Bundle.CachedInfoFilePath;
string dataFilePath = _bundleInfo.Bundle.CachedDataFilePath;
string dataFileCRC = _bundleInfo.Bundle.FileCRC;
long dataFileSize = _bundleInfo.Bundle.FileSize;
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
FileInfo fileInfo = new FileInfo(tempFilePath);
fileInfo.MoveTo(dataFilePath);
// 写入信息文件记录验证数据
CacheFileInfo.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize);
// 记录缓存文件
var wrapper = new PackageCache.RecordWrapper(infoFilePath, dataFilePath, dataFileCRC, dataFileSize);
CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper);
}
} }
} }

View File

@ -0,0 +1,225 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 普通的下载器
/// </summary>
internal sealed class FileGeneralDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private readonly string _tempFilePath;
private VerifyTempFileOperation _verifyFileOp = null;
private ESteps _steps = ESteps.None;
public FileGeneralDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{
_tempFilePath = bundleInfo.Bundle.TempDataFilePath;
}
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 准备下载
if (_steps == ESteps.PrepareDownload)
{
// 重置变量
_downloadProgress = 0f;
_downloadedBytes = 0;
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
_tryAgainTimer = 0f;
// 删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
_steps = ESteps.CreateDownloader;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
_webRequest = DownloadSystem.NewRequest(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_downloadProgress = _webRequest.downloadProgress;
_downloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckTimeout();
return;
}
bool hasError = false;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#endif
// 如果网络异常
if (hasError)
{
// 下载失败之后删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.VerifyTempFile;
}
// 最终释放下载器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_verifyFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_verifyFileOp);
_steps = ESteps.WaitingVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.WaitingVerifyTempFile)
{
if (WaitForAsyncComplete)
_verifyFileOp.Update();
if (_verifyFileOp.IsDone == false)
return;
if (_verifyFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_lastError = _verifyFileOp.Error;
_steps = ESteps.TryAgain;
}
}
// 缓存下载文件
if (_steps == ESteps.CachingFile)
{
try
{
CachingFile(_tempFilePath);
_status = EStatus.Succeed;
_steps = ESteps.Done;
_lastError = string.Empty;
_lastCode = 0;
}
catch (Exception e)
{
_lastError = e.Message;
_steps = ESteps.TryAgain;
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
YooLogger.Warning($"Try again download : {_requestURL}");
}
}
}
public override void Abort()
{
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort";
_lastCode = 0;
DisposeWebRequest();
}
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@ -7,28 +7,47 @@ using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal sealed class FileDownloader : DownloaderBase /// <summary>
/// 断点续传下载器
/// </summary>
internal sealed class FileResumeDownloader : DownloaderBase
{ {
private readonly bool _breakResume; private enum ESteps
{
None,
CheckTempFile,
WaitingCheckTempFile,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private readonly string _tempFilePath; private readonly string _tempFilePath;
private UnityWebRequest _webRequest = null;
private DownloadHandlerFileRange _downloadHandle = null; private DownloadHandlerFileRange _downloadHandle = null;
private VerifyTempFileOperation _checkFileOp = null; private VerifyTempFileOperation _checkFileOp = null;
private VerifyTempFileOperation _verifyFileOp = null; private VerifyTempFileOperation _verifyFileOp = null;
private ESteps _steps = ESteps.None;
// 重置变量 // 重置变量
private bool _isAbort = false;
private ulong _fileOriginLength; private ulong _fileOriginLength;
private ulong _latestDownloadBytes;
private float _latestDownloadRealtime;
private float _tryAgainTimer;
public FileDownloader(BundleInfo bundleInfo, bool breakResume) : base(bundleInfo) public FileResumeDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{ {
_breakResume = breakResume;
_tempFilePath = bundleInfo.Bundle.TempDataFilePath; _tempFilePath = bundleInfo.Bundle.TempDataFilePath;
} }
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
_steps = ESteps.CheckTempFile;
}
}
public override void Update() public override void Update()
{ {
if (_steps == ESteps.None) if (_steps == ESteps.None)
@ -39,7 +58,7 @@ namespace YooAsset
// 检测临时文件 // 检测临时文件
if (_steps == ESteps.CheckTempFile) if (_steps == ESteps.CheckTempFile)
{ {
VerifyTempElement element = new VerifyTempElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_checkFileOp = VerifyTempFileOperation.CreateOperation(element); _checkFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_checkFileOp); OperationSystem.StartOperation(_checkFileOp);
_steps = ESteps.WaitingCheckTempFile; _steps = ESteps.WaitingCheckTempFile;
@ -75,38 +94,21 @@ namespace YooAsset
// 重置变量 // 重置变量
_downloadProgress = 0f; _downloadProgress = 0f;
_downloadedBytes = 0; _downloadedBytes = 0;
// 重置变量
_isAbort = false; _isAbort = false;
_fileOriginLength = 0;
_latestDownloadBytes = 0; _latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup; _latestDownloadRealtime = Time.realtimeSinceStartup;
_tryAgainTimer = 0f; _tryAgainTimer = 0f;
_fileOriginLength = 0;
// 获取请求地址 // 获取请求地址
_requestURL = GetRequestURL(); _requestURL = GetRequestURL();
_steps = ESteps.CreateDownloader;
if (_breakResume)
_steps = ESteps.CreateResumeDownloader;
else
_steps = ESteps.CreateGeneralDownloader;
} }
// 创建普通的下载器 // 创建下载器
if (_steps == ESteps.CreateGeneralDownloader) if (_steps == ESteps.CreateDownloader)
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_webRequest = DownloadSystem.NewRequest(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 创建断点续传下载器
if (_steps == ESteps.CreateResumeDownloader)
{ {
long fileLength = -1; long fileLength = -1;
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
@ -166,8 +168,6 @@ namespace YooAsset
// 如果网络异常 // 如果网络异常
if (hasError) if (hasError)
{
if (_breakResume)
{ {
// 注意:下载断点续传文件发生特殊错误码之后删除文件 // 注意:下载断点续传文件发生特殊错误码之后删除文件
if (DownloadSystem.ClearFileResponseCodes != null) if (DownloadSystem.ClearFileResponseCodes != null)
@ -178,13 +178,6 @@ namespace YooAsset
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
} }
} }
}
else
{
// 注意:非断点续传下载失败之后删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
_steps = ESteps.TryAgain; _steps = ESteps.TryAgain;
} }
@ -193,14 +186,14 @@ namespace YooAsset
_steps = ESteps.VerifyTempFile; _steps = ESteps.VerifyTempFile;
} }
// 释放下载器 // 最终释放下载器
DisposeWebRequest(); DisposeWebRequest();
} }
// 验证下载文件 // 验证下载文件
if (_steps == ESteps.VerifyTempFile) if (_steps == ESteps.VerifyTempFile)
{ {
VerifyTempElement element = new VerifyTempElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize); VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_verifyFileOp = VerifyTempFileOperation.CreateOperation(element); _verifyFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_verifyFileOp); OperationSystem.StartOperation(_verifyFileOp);
_steps = ESteps.WaitingVerifyTempFile; _steps = ESteps.WaitingVerifyTempFile;
@ -234,29 +227,11 @@ namespace YooAsset
{ {
try try
{ {
string infoFilePath = _bundleInfo.Bundle.CachedInfoFilePath; CachingFile(_tempFilePath);
string dataFilePath = _bundleInfo.Bundle.CachedDataFilePath; _status = EStatus.Succeed;
string dataFileCRC = _bundleInfo.Bundle.FileCRC; _steps = ESteps.Done;
long dataFileSize = _bundleInfo.Bundle.FileSize;
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
FileInfo fileInfo = new FileInfo(_tempFilePath);
fileInfo.MoveTo(dataFilePath);
// 写入信息文件记录验证数据
CacheFileInfo.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize);
// 记录缓存文件
var wrapper = new PackageCache.RecordWrapper(infoFilePath, dataFilePath, dataFileCRC, dataFileSize);
CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper);
_lastError = string.Empty; _lastError = string.Empty;
_lastCode = 0; _lastCode = 0;
_steps = ESteps.Succeed;
} }
catch (Exception e) catch (Exception e)
{ {
@ -271,7 +246,8 @@ namespace YooAsset
if (_failedTryAgain <= 0) if (_failedTryAgain <= 0)
{ {
ReportError(); ReportError();
_steps = ESteps.Failed; _status = EStatus.Failed;
_steps = ESteps.Done;
return; return;
} }
@ -289,33 +265,14 @@ namespace YooAsset
{ {
if (IsDone() == false) if (IsDone() == false)
{ {
_steps = ESteps.Failed; _status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort"; _lastError = "user abort";
_lastCode = 0; _lastCode = 0;
DisposeWebRequest(); DisposeWebRequest();
} }
} }
private void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != DownloadedBytes)
{
_latestDownloadBytes = DownloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
YooLogger.Warning($"Web file request timeout : {_requestURL}");
_webRequest.Abort();
_isAbort = true;
}
}
}
private void DisposeWebRequest() private void DisposeWebRequest()
{ {
if (_downloadHandle != null) if (_downloadHandle != null)

View File

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

View File

@ -1,20 +0,0 @@

namespace YooAsset
{
internal sealed class TempDownloader : DownloaderBase
{
public TempDownloader(BundleInfo bundleInfo) : base(bundleInfo)
{
_downloadProgress = 1f;
_downloadedBytes = (ulong)bundleInfo.Bundle.FileSize;
_steps = ESteps.Succeed;
}
public override void Update()
{
}
public override void Abort()
{
}
}
}

View File

@ -0,0 +1,209 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class WebDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private bool _keepDownloadHandleLife = false;
private DownloadHandlerAssetBundle _downloadhandler;
private ESteps _steps = ESteps.None;
public WebDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{
}
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
_keepDownloadHandleLife = (bool)param[0];
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 创建下载器
if (_steps == ESteps.PrepareDownload)
{
// 重置变量
_downloadProgress = 0f;
_downloadedBytes = 0;
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
_tryAgainTimer = 0f;
// 获取请求地址
_requestURL = GetRequestURL();
_steps = ESteps.CreateDownloader;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
_webRequest = DownloadSystem.NewRequest(_requestURL);
if (CacheSystem.DisableUnityCacheOnWebGL)
{
uint crc = _bundleInfo.Bundle.UnityCRC;
_downloadhandler = new DownloadHandlerAssetBundle(_requestURL, crc);
_downloadhandler.autoLoadAssetBundle = false;
}
else
{
uint crc = _bundleInfo.Bundle.UnityCRC;
var hash = Hash128.Parse(_bundleInfo.Bundle.FileHash);
_downloadhandler = new DownloadHandlerAssetBundle(_requestURL, hash, crc);
_downloadhandler.autoLoadAssetBundle = false;
}
_webRequest.downloadHandler = _downloadhandler;
_webRequest.disposeDownloadHandlerOnDispose = false;
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_downloadProgress = _webRequest.downloadProgress;
_downloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckTimeout();
return;
}
bool hasError = false;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#endif
// 如果网络异常
if (hasError)
{
_steps = ESteps.TryAgain;
}
else
{
_status = EStatus.Succeed;
_steps = ESteps.Done;
_lastError = string.Empty;
_lastCode = 0;
}
// 最终释放请求
DisposeRequest();
if (_keepDownloadHandleLife == false)
DisposeHandler();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
DisposeRequest();
DisposeHandler();
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
YooLogger.Warning($"Try again download : {_requestURL}");
}
}
}
public override void Abort()
{
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort";
_lastCode = 0;
DisposeRequest();
DisposeHandler();
}
}
private void DisposeRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
/// <summary>
/// 获取资源包
/// </summary>
public AssetBundle GetAssetBundle()
{
if (_downloadhandler != null)
return _downloadhandler.assetBundle;
return null;
}
/// <summary>
/// 释放下载句柄
/// </summary>
public void DisposeHandler()
{
if (_downloadhandler != null)
{
_downloadhandler.Dispose();
_downloadhandler = null;
}
}
}
}

View File

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

View File

@ -2,38 +2,26 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine.Networking; using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> internal class UnityWebDataRequester : UnityWebRequesterBase
/// 下载器
/// 说明UnityWebRequest(UWR) supports reading streaming assets since 2017.1
/// </summary>
internal class UnityWebDataRequester
{ {
private UnityWebRequest _webRequest;
private UnityWebRequestAsyncOperation _operationHandle;
/// <summary>
/// 请求URL地址
/// </summary>
public string URL { private set; get; }
/// <summary> /// <summary>
/// 发送GET请求 /// 发送GET请求
/// </summary> /// </summary>
/// <param name="timeout">超时:从请求开始计时</param> public void SendRequest(string url, int timeout = 60)
public void SendRequest(string url, int timeout = 0)
{ {
if (_webRequest == null) if (_webRequest == null)
{ {
URL = url; URL = url;
ResetTimeout(timeout);
_webRequest = DownloadSystem.NewRequest(URL); _webRequest = DownloadSystem.NewRequest(URL);
DownloadHandlerBuffer handler = new DownloadHandlerBuffer(); DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
_webRequest.downloadHandler = handler; _webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true; _webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.timeout = timeout;
_operationHandle = _webRequest.SendWebRequest(); _operationHandle = _webRequest.SendWebRequest();
} }
} }
@ -59,65 +47,5 @@ namespace YooAsset
else else
return null; return null;
} }
/// <summary>
/// 释放下载器
/// </summary>
public void Dispose()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
_operationHandle = null;
}
}
/// <summary>
/// 是否完毕(无论成功失败)
/// </summary>
public bool IsDone()
{
if (_operationHandle == null)
return false;
return _operationHandle.isDone;
}
/// <summary>
/// 下载进度
/// </summary>
public float Progress()
{
if (_operationHandle == null)
return 0;
return _operationHandle.progress;
}
/// <summary>
/// 下载是否发生错误
/// </summary>
public bool HasError()
{
#if UNITY_2020_3_OR_NEWER
return _webRequest.result != UnityWebRequest.Result.Success;
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
return true;
else
return false;
#endif
}
/// <summary>
/// 获取错误信息
/// </summary>
public string GetError()
{
if (_webRequest != null)
{
return $"URL : {URL} Error : {_webRequest.error}";
}
return string.Empty;
}
} }
} }

View File

@ -6,129 +6,25 @@ using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> internal class UnityWebFileRequester : UnityWebRequesterBase
/// 下载器
/// 说明UnityWebRequest(UWR) supports reading streaming assets since 2017.1
/// </summary>
internal class UnityWebFileRequester
{ {
private UnityWebRequest _webRequest;
private UnityWebRequestAsyncOperation _operationHandle;
// 超时相关
private float _timeout;
private bool _isAbort = false;
private ulong _latestDownloadBytes;
private float _latestDownloadRealtime;
/// <summary>
/// 请求URL地址
/// </summary>
public string URL { private set; get; }
/// <summary> /// <summary>
/// 发送GET请求 /// 发送GET请求
/// </summary> /// </summary>
public void SendRequest(string url, string savePath, float timeout = 60) public void SendRequest(string url, string fileSavePath, int timeout = 60)
{ {
if (_webRequest == null) if (_webRequest == null)
{ {
URL = url; URL = url;
_timeout = timeout; ResetTimeout(timeout);
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
_webRequest = DownloadSystem.NewRequest(URL); _webRequest = DownloadSystem.NewRequest(URL);
DownloadHandlerFile handler = new DownloadHandlerFile(savePath); DownloadHandlerFile handler = new DownloadHandlerFile(fileSavePath);
handler.removeFileOnAbort = true; handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler; _webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true; _webRequest.disposeDownloadHandlerOnDispose = true;
_operationHandle = _webRequest.SendWebRequest(); _operationHandle = _webRequest.SendWebRequest();
} }
} }
/// <summary>
/// 释放下载器
/// </summary>
public void Dispose()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
_operationHandle = null;
}
}
/// <summary>
/// 是否完毕(无论成功失败)
/// </summary>
public bool IsDone()
{
if (_operationHandle == null)
return false;
return _operationHandle.isDone;
}
/// <summary>
/// 下载进度
/// </summary>
public float Progress()
{
if (_operationHandle == null)
return 0;
return _operationHandle.progress;
}
/// <summary>
/// 下载是否发生错误
/// </summary>
public bool HasError()
{
#if UNITY_2020_3_OR_NEWER
return _webRequest.result != UnityWebRequest.Result.Success;
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
return true;
else
return false;
#endif
}
/// <summary>
/// 获取错误信息
/// </summary>
public string GetError()
{
if (_webRequest != null)
{
return $"URL : {URL} Error : {_webRequest.error}";
}
return string.Empty;
}
/// <summary>
/// 检测超时
/// </summary>
public void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != _webRequest.downloadedBytes)
{
_latestDownloadBytes = _webRequest.downloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
_webRequest.Abort();
_isAbort = true;
}
}
}
} }
} }

View File

@ -0,0 +1,116 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using UnityEngine.Networking;
using UnityEngine;
namespace YooAsset
{
internal abstract class UnityWebRequesterBase
{
protected UnityWebRequest _webRequest;
protected UnityWebRequestAsyncOperation _operationHandle;
// 超时相关
private float _timeout;
private bool _isAbort = false;
private ulong _latestDownloadBytes;
private float _latestDownloadRealtime;
/// <summary>
/// 请求URL地址
/// </summary>
public string URL { protected set; get; }
protected void ResetTimeout(float timeout)
{
_timeout = timeout;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
/// <summary>
/// 释放下载器
/// </summary>
public void Dispose()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
_operationHandle = null;
}
}
/// <summary>
/// 是否完毕(无论成功失败)
/// </summary>
public bool IsDone()
{
if (_operationHandle == null)
return false;
return _operationHandle.isDone;
}
/// <summary>
/// 下载进度
/// </summary>
public float Progress()
{
if (_operationHandle == null)
return 0;
return _operationHandle.progress;
}
/// <summary>
/// 下载是否发生错误
/// </summary>
public bool HasError()
{
#if UNITY_2020_3_OR_NEWER
return _webRequest.result != UnityWebRequest.Result.Success;
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
return true;
else
return false;
#endif
}
/// <summary>
/// 获取错误信息
/// </summary>
public string GetError()
{
if (_webRequest != null)
{
return $"URL : {URL} Error : {_webRequest.error}";
}
return string.Empty;
}
/// <summary>
/// 检测超时
/// </summary>
public void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != _webRequest.downloadedBytes)
{
_latestDownloadBytes = _webRequest.downloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
_webRequest.Abort();
_isAbort = true;
}
}
}
}
}

View File

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

View File

@ -20,6 +20,11 @@ namespace YooAsset
/// 联机运行模式 /// 联机运行模式
/// </summary> /// </summary>
HostPlayMode, HostPlayMode,
/// <summary>
/// WebGL运行模式
/// </summary>
WebPlayMode,
} }
/// <summary> /// <summary>
@ -90,4 +95,20 @@ namespace YooAsset
/// </summary> /// </summary>
public IRemoteServices RemoteServices = null; public IRemoteServices RemoteServices = null;
} }
/// <summary>
/// WebGL运行模式的初始化参数
/// </summary>
public class WebPlayModeParameters : InitializeParameters
{
/// <summary>
/// 内置资源查询服务接口
/// </summary>
public IQueryServices QueryServices = null;
/// <summary>
/// 远端资源地址查询服务类
/// </summary>
public IRemoteServices RemoteServices = null;
}
} }

View File

@ -61,6 +61,7 @@ namespace YooAsset
{ {
var packageBundle = manifest.BundleList[i]; var packageBundle = manifest.BundleList[i];
buffer.WriteUTF8(packageBundle.BundleName); buffer.WriteUTF8(packageBundle.BundleName);
buffer.WriteUInt32(packageBundle.UnityCRC);
buffer.WriteUTF8(packageBundle.FileHash); buffer.WriteUTF8(packageBundle.FileHash);
buffer.WriteUTF8(packageBundle.FileCRC); buffer.WriteUTF8(packageBundle.FileCRC);
buffer.WriteInt64(packageBundle.FileSize); buffer.WriteInt64(packageBundle.FileSize);
@ -139,6 +140,7 @@ namespace YooAsset
{ {
var packageBundle = new PackageBundle(); var packageBundle = new PackageBundle();
packageBundle.BundleName = buffer.ReadUTF8(); packageBundle.BundleName = buffer.ReadUTF8();
packageBundle.UnityCRC = buffer.ReadUInt32();
packageBundle.FileHash = buffer.ReadUTF8(); packageBundle.FileHash = buffer.ReadUTF8();
packageBundle.FileCRC = buffer.ReadUTF8(); packageBundle.FileCRC = buffer.ReadUTF8();
packageBundle.FileSize = buffer.ReadInt64(); packageBundle.FileSize = buffer.ReadInt64();
@ -208,7 +210,7 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 批量转换解压BundleInfo /// 批量转换解压BundleInfo
/// </summary> /// </summary>
public static List<BundleInfo> ConvertToUnpackInfos(List<PackageBundle> unpackList) public static List<BundleInfo> ConvertToUnpackInfos(List<PackageBundle> unpackList)
{ {

View File

@ -135,8 +135,6 @@ namespace YooAsset
if (downloader.IsDone() == false) if (downloader.IsDone() == false)
continue; continue;
BundleInfo bundleInfo = downloader.GetBundleInfo();
// 检测是否下载失败 // 检测是否下载失败
if (downloader.HasError()) if (downloader.HasError())
{ {
@ -148,7 +146,7 @@ namespace YooAsset
// 下载成功 // 下载成功
_removeList.Add(downloader); _removeList.Add(downloader);
_cachedDownloadCount++; _cachedDownloadCount++;
_cachedDownloadBytes += bundleInfo.Bundle.FileSize; _cachedDownloadBytes += downloader.GetDownloadFileSize();
} }
// 移除已经完成的下载器(无论成功或失败) // 移除已经完成的下载器(无论成功或失败)
@ -177,8 +175,9 @@ namespace YooAsset
{ {
int index = _downloadList.Count - 1; int index = _downloadList.Count - 1;
var bundleInfo = _downloadList[index]; var bundleInfo = _downloadList[index];
var operation = DownloadSystem.BeginDownload(bundleInfo, _failedTryAgain, _timeout); var downloader = DownloadSystem.CreateDownload(bundleInfo, _failedTryAgain, _timeout);
_downloaders.Add(operation); downloader.SendRequest();
_downloaders.Add(downloader);
_downloadList.RemoveAt(index); _downloadList.RemoveAt(index);
OnStartDownloadFileCallback?.Invoke(bundleInfo.Bundle.BundleName, bundleInfo.Bundle.FileSize); OnStartDownloadFileCallback?.Invoke(bundleInfo.Bundle.BundleName, bundleInfo.Bundle.FileSize);
} }
@ -190,7 +189,7 @@ namespace YooAsset
if (_failedList.Count > 0) if (_failedList.Count > 0)
{ {
var failedDownloader = _failedList[0]; var failedDownloader = _failedList[0];
string fileName = failedDownloader.GetBundleInfo().Bundle.BundleName; string fileName = failedDownloader.GetDownloadBundleName();
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = $"Failed to download file : {fileName}"; Error = $"Failed to download file : {fileName}";

View File

@ -364,6 +364,93 @@ namespace YooAsset
} }
} }
/// <summary>
/// WebGL运行模式的初始化操作
/// </summary>
internal sealed class WebPlayModeInitializationOperation : InitializationOperation
{
private enum ESteps
{
None,
QueryWebPackageVersion,
LoadWebManifest,
Done,
}
private readonly WebPlayModeImpl _impl;
private readonly string _packageName;
private QueryBuildinPackageVersionOperation _queryWebPackageVersionOp;
private LoadBuildinManifestOperation _loadWebManifestOp;
private ESteps _steps = ESteps.None;
internal WebPlayModeInitializationOperation(WebPlayModeImpl impl, string packageName)
{
_impl = impl;
_packageName = packageName;
}
internal override void Start()
{
_steps = ESteps.QueryWebPackageVersion;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.QueryWebPackageVersion)
{
if (_queryWebPackageVersionOp == null)
{
_queryWebPackageVersionOp = new QueryBuildinPackageVersionOperation(_packageName);
OperationSystem.StartOperation(_queryWebPackageVersionOp);
}
if (_queryWebPackageVersionOp.IsDone == false)
return;
if (_queryWebPackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.LoadWebManifest;
}
else
{
// 注意WebGL平台可能因为网络的原因会导致请求失败。如果内置清单不存在或者超时也不需要报错
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
string error = _queryWebPackageVersionOp.Error;
YooLogger.Log($"Failed to load web package version file : {error}");
}
}
if (_steps == ESteps.LoadWebManifest)
{
if (_loadWebManifestOp == null)
{
_loadWebManifestOp = new LoadBuildinManifestOperation(_packageName, _queryWebPackageVersionOp.PackageVersion);
OperationSystem.StartOperation(_loadWebManifestOp);
}
Progress = _loadWebManifestOp.Progress;
if (_loadWebManifestOp.IsDone == false)
return;
if (_loadWebManifestOp.Status == EOperationStatus.Succeed)
{
PackageVersion = _loadWebManifestOp.Manifest.PackageVersion;
_impl.ActiveManifest = _loadWebManifestOp.Manifest;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadWebManifestOp.Error;
}
}
}
}
/// <summary> /// <summary>
/// 应用程序水印 /// 应用程序水印
/// </summary> /// </summary>

View File

@ -196,6 +196,7 @@ namespace YooAsset
{ {
var packageBundle = new PackageBundle(); var packageBundle = new PackageBundle();
packageBundle.BundleName = _buffer.ReadUTF8(); packageBundle.BundleName = _buffer.ReadUTF8();
packageBundle.UnityCRC = _buffer.ReadUInt32();
packageBundle.FileHash = _buffer.ReadUTF8(); packageBundle.FileHash = _buffer.ReadUTF8();
packageBundle.FileCRC = _buffer.ReadUTF8(); packageBundle.FileCRC = _buffer.ReadUTF8();
packageBundle.FileSize = _buffer.ReadInt64(); packageBundle.FileSize = _buffer.ReadInt64();

View File

@ -0,0 +1,150 @@

namespace YooAsset
{
internal class LoadRemoteManifestOperation : AsyncOperationBase
{
private enum ESteps
{
None,
DownloadPackageHashFile,
DownloadManifestFile,
VerifyFileHash,
CheckDeserializeManifest,
Done,
}
private static int RequestCount = 0;
private readonly IRemoteServices _remoteServices;
private readonly string _packageName;
private readonly string _packageVersion;
private readonly int _timeout;
private QueryRemotePackageHashOperation _queryRemotePackageHashOp;
private UnityWebDataRequester _downloader;
private DeserializeManifestOperation _deserializer;
private byte[] _fileData;
private ESteps _steps = ESteps.None;
/// <summary>
/// 加载的清单实例
/// </summary>
public PackageManifest Manifest { private set; get; }
internal LoadRemoteManifestOperation(IRemoteServices remoteServices, string packageName, string packageVersion, int timeout)
{
_remoteServices = remoteServices;
_packageName = packageName;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void Start()
{
RequestCount++;
_steps = ESteps.DownloadPackageHashFile;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadPackageHashFile)
{
if (_queryRemotePackageHashOp == null)
{
_queryRemotePackageHashOp = new QueryRemotePackageHashOperation(_remoteServices, _packageName, _packageVersion, _timeout);
OperationSystem.StartOperation(_queryRemotePackageHashOp);
}
if (_queryRemotePackageHashOp.IsDone == false)
return;
if (_queryRemotePackageHashOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.DownloadManifestFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _queryRemotePackageHashOp.Error;
}
}
if (_steps == ESteps.DownloadManifestFile)
{
if (_downloader == null)
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(_packageName, _packageVersion);
string webURL = GetDownloadRequestURL(fileName);
YooLogger.Log($"Beginning to download manifest file : {webURL}");
_downloader = new UnityWebDataRequester();
_downloader.SendRequest(webURL, _timeout);
}
_downloader.CheckTimeout();
if (_downloader.IsDone() == false)
return;
if (_downloader.HasError())
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloader.GetError();
}
else
{
_fileData = _downloader.GetData();
_steps = ESteps.VerifyFileHash;
}
_downloader.Dispose();
}
if (_steps == ESteps.VerifyFileHash)
{
string fileHash = HashUtility.BytesMD5(_fileData);
if (fileHash != _queryRemotePackageHashOp.PackageHash)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Failed to verify remote manifest file hash !";
}
else
{
_deserializer = new DeserializeManifestOperation(_fileData);
OperationSystem.StartOperation(_deserializer);
_steps = ESteps.CheckDeserializeManifest;
}
}
if (_steps == ESteps.CheckDeserializeManifest)
{
Progress = _deserializer.Progress;
if (_deserializer.IsDone == false)
return;
if (_deserializer.Status == EOperationStatus.Succeed)
{
Manifest = _deserializer.Manifest;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _deserializer.Error;
}
}
}
private string GetDownloadRequestURL(string fileName)
{
// 轮流返回请求地址
if (RequestCount % 2 == 0)
return _remoteServices.GetRemoteFallbackURL(fileName);
else
return _remoteServices.GetRemoteMainURL(fileName);
}
}
}

View File

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

View File

@ -0,0 +1,99 @@

namespace YooAsset
{
internal class QueryRemotePackageHashOperation : AsyncOperationBase
{
private enum ESteps
{
None,
DownloadPackageHash,
Done,
}
private static int RequestCount = 0;
private readonly IRemoteServices _remoteServices;
private readonly string _packageName;
private readonly string _packageVersion;
private readonly int _timeout;
private UnityWebDataRequester _downloader;
private ESteps _steps = ESteps.None;
/// <summary>
/// 包裹哈希值
/// </summary>
public string PackageHash { private set; get; }
public QueryRemotePackageHashOperation(IRemoteServices remoteServices, string packageName, string packageVersion, int timeout)
{
_remoteServices = remoteServices;
_packageName = packageName;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void Start()
{
RequestCount++;
_steps = ESteps.DownloadPackageHash;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.DownloadPackageHash)
{
if (_downloader == null)
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(_packageName, _packageVersion);
string webURL = GetPackageHashRequestURL(fileName);
YooLogger.Log($"Beginning to request package hash : {webURL}");
_downloader = new UnityWebDataRequester();
_downloader.SendRequest(webURL, _timeout);
}
Progress = _downloader.Progress();
_downloader.CheckTimeout();
if (_downloader.IsDone() == false)
return;
if (_downloader.HasError())
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _downloader.GetError();
}
else
{
PackageHash = _downloader.GetText();
if (string.IsNullOrEmpty(PackageHash))
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Remote package hash is empty : {_downloader.URL}";
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
_downloader.Dispose();
}
}
private string GetPackageHashRequestURL(string fileName)
{
string url;
// 轮流返回请求地址
if (RequestCount % 2 == 0)
url = _remoteServices.GetRemoteFallbackURL(fileName);
else
url = _remoteServices.GetRemoteMainURL(fileName);
return url;
}
}
}

View File

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

View File

@ -1,5 +1,4 @@
using System.IO; 
namespace YooAsset namespace YooAsset
{ {
internal class QueryRemotePackageVersionOperation : AsyncOperationBase internal class QueryRemotePackageVersionOperation : AsyncOperationBase
@ -54,6 +53,7 @@ namespace YooAsset
} }
Progress = _downloader.Progress(); Progress = _downloader.Progress();
_downloader.CheckTimeout();
if (_downloader.IsDone() == false) if (_downloader.IsDone() == false)
return; return;

View File

@ -286,4 +286,14 @@ namespace YooAsset
return operation; return operation;
} }
} }
internal class WebPlayModePreDownloadContentOperation : PreDownloadContentOperation
{
internal override void Start()
{
Status = EOperationStatus.Succeed;
}
internal override void Update()
{
}
}
} }

View File

@ -189,4 +189,82 @@ namespace YooAsset
_impl.FlushManifestVersionFile(); _impl.FlushManifestVersionFile();
} }
} }
/// <summary>
/// WebGL模式的更新清单操作
/// </summary>
internal sealed class WebPlayModeUpdatePackageManifestOperation : UpdatePackageManifestOperation
{
private enum ESteps
{
None,
CheckActiveManifest,
LoadRemoteManifest,
Done,
}
private readonly WebPlayModeImpl _impl;
private readonly string _packageName;
private readonly string _packageVersion;
private readonly int _timeout;
private LoadRemoteManifestOperation _loadCacheManifestOp;
private ESteps _steps = ESteps.None;
internal WebPlayModeUpdatePackageManifestOperation(WebPlayModeImpl impl, string packageName, string packageVersion, int timeout)
{
_impl = impl;
_packageName = packageName;
_packageVersion = packageVersion;
_timeout = timeout;
}
internal override void Start()
{
_steps = ESteps.CheckActiveManifest;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckActiveManifest)
{
// 检测当前激活的清单对象
if (_impl.ActiveManifest != null && _impl.ActiveManifest.PackageVersion == _packageVersion)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.LoadRemoteManifest;
}
}
if (_steps == ESteps.LoadRemoteManifest)
{
if (_loadCacheManifestOp == null)
{
_loadCacheManifestOp = new LoadRemoteManifestOperation(_impl.RemoteServices, _packageName, _packageVersion, _timeout);
OperationSystem.StartOperation(_loadCacheManifestOp);
}
if (_loadCacheManifestOp.IsDone == false)
return;
if (_loadCacheManifestOp.Status == EOperationStatus.Succeed)
{
_impl.ActiveManifest = _loadCacheManifestOp.Manifest;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadCacheManifestOp.Error;
}
}
}
}
} }

View File

@ -104,4 +104,66 @@ namespace YooAsset
} }
} }
} }
/// <summary>
/// WebGL模式的请求远端包裹的最新版本
/// </summary>
internal sealed class WebPlayModeUpdatePackageVersionOperation : UpdatePackageVersionOperation
{
private enum ESteps
{
None,
QueryRemotePackageVersion,
Done,
}
private readonly WebPlayModeImpl _impl;
private readonly string _packageName;
private readonly bool _appendTimeTicks;
private readonly int _timeout;
private QueryRemotePackageVersionOperation _queryRemotePackageVersionOp;
private ESteps _steps = ESteps.None;
internal WebPlayModeUpdatePackageVersionOperation(WebPlayModeImpl impl, string packageName, bool appendTimeTicks, int timeout)
{
_impl = impl;
_packageName = packageName;
_appendTimeTicks = appendTimeTicks;
_timeout = timeout;
}
internal override void Start()
{
_steps = ESteps.QueryRemotePackageVersion;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.QueryRemotePackageVersion)
{
if (_queryRemotePackageVersionOp == null)
{
_queryRemotePackageVersionOp = new QueryRemotePackageVersionOperation(_impl.RemoteServices, _packageName, _appendTimeTicks, _timeout);
OperationSystem.StartOperation(_queryRemotePackageVersionOp);
}
if (_queryRemotePackageVersionOp.IsDone == false)
return;
if (_queryRemotePackageVersionOp.Status == EOperationStatus.Succeed)
{
PackageVersion = _queryRemotePackageVersionOp.PackageVersion;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _queryRemotePackageVersionOp.Error;
}
}
}
}
} }

View File

@ -11,6 +11,11 @@ namespace YooAsset
/// </summary> /// </summary>
public string BundleName; public string BundleName;
/// <summary>
/// Unity引擎生成的CRC
/// </summary>
public uint UnityCRC;
/// <summary> /// <summary>
/// 文件哈希值 /// 文件哈希值
/// </summary> /// </summary>

View File

@ -0,0 +1,157 @@
using System;
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal class WebPlayModeImpl : IPlayModeServices, IBundleServices
{
private PackageManifest _activeManifest;
// 参数相关
private string _packageName;
private IQueryServices _queryServices;
private IRemoteServices _remoteServices;
public IRemoteServices RemoteServices
{
get { return _remoteServices; }
}
/// <summary>
/// 异步初始化
/// </summary>
public InitializationOperation InitializeAsync(string packageName, IQueryServices queryServices, IRemoteServices remoteServices)
{
_packageName = packageName;
_queryServices = queryServices;
_remoteServices = remoteServices;
var operation = new WebPlayModeInitializationOperation(this, packageName);
OperationSystem.StartOperation(operation);
return operation;
}
// 下载相关
private BundleInfo ConvertToDownloadInfo(PackageBundle packageBundle)
{
string remoteMainURL = _remoteServices.GetRemoteMainURL(packageBundle.FileName);
string remoteFallbackURL = _remoteServices.GetRemoteFallbackURL(packageBundle.FileName);
BundleInfo bundleInfo = new BundleInfo(packageBundle, BundleInfo.ELoadMode.LoadFromRemote, remoteMainURL, remoteFallbackURL);
return bundleInfo;
}
#region IPlayModeServices接口
public PackageManifest ActiveManifest
{
set
{
_activeManifest = value;
}
get
{
return _activeManifest;
}
}
public void FlushManifestVersionFile()
{
}
private bool IsBuildinPackageBundle(PackageBundle packageBundle)
{
return _queryServices.QueryStreamingAssets(_packageName, packageBundle.FileName);
}
UpdatePackageVersionOperation IPlayModeServices.UpdatePackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new WebPlayModeUpdatePackageVersionOperation(this, _packageName, appendTimeTicks, timeout);
OperationSystem.StartOperation(operation);
return operation;
}
UpdatePackageManifestOperation IPlayModeServices.UpdatePackageManifestAsync(string packageVersion, bool autoSaveVersion, int timeout)
{
var operation = new WebPlayModeUpdatePackageManifestOperation(this, _packageName, packageVersion, timeout);
OperationSystem.StartOperation(operation);
return operation;
}
PreDownloadContentOperation IPlayModeServices.PreDownloadContentAsync(string packageVersion, int timeout)
{
var operation = new WebPlayModePreDownloadContentOperation();
OperationSystem.StartOperation(operation);
return operation;
}
ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout)
{
return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout);
}
ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByTags(string[] tags, int downloadingMaxNumber, int failedTryAgain, int timeout)
{
return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout);
}
ResourceDownloaderOperation IPlayModeServices.CreateResourceDownloaderByPaths(AssetInfo[] assetInfos, int downloadingMaxNumber, int failedTryAgain, int timeout)
{
return ResourceDownloaderOperation.CreateEmptyDownloader(downloadingMaxNumber, failedTryAgain, timeout);
}
ResourceUnpackerOperation IPlayModeServices.CreateResourceUnpackerByAll(int upackingMaxNumber, int failedTryAgain, int timeout)
{
return ResourceUnpackerOperation.CreateEmptyUnpacker(upackingMaxNumber, failedTryAgain, timeout);
}
ResourceUnpackerOperation IPlayModeServices.CreateResourceUnpackerByTags(string[] tags, int upackingMaxNumber, int failedTryAgain, int timeout)
{
return ResourceUnpackerOperation.CreateEmptyUnpacker(upackingMaxNumber, failedTryAgain, timeout);
}
#endregion
#region IBundleServices接口
private BundleInfo CreateBundleInfo(PackageBundle packageBundle)
{
if (packageBundle == null)
throw new Exception("Should never get here !");
// 查询APP资源
if (IsBuildinPackageBundle(packageBundle))
{
BundleInfo bundleInfo = new BundleInfo(packageBundle, BundleInfo.ELoadMode.LoadFromStreaming);
return bundleInfo;
}
// 从服务端下载
return ConvertToDownloadInfo(packageBundle);
}
BundleInfo IBundleServices.GetBundleInfo(AssetInfo assetInfo)
{
if (assetInfo.IsInvalid)
throw new Exception("Should never get here !");
// 注意:如果清单里未找到资源包会抛出异常!
var packageBundle = _activeManifest.GetMainPackageBundle(assetInfo.AssetPath);
return CreateBundleInfo(packageBundle);
}
BundleInfo[] IBundleServices.GetAllDependBundleInfos(AssetInfo assetInfo)
{
if (assetInfo.IsInvalid)
throw new Exception("Should never get here !");
// 注意:如果清单里未找到资源包会抛出异常!
var depends = _activeManifest.GetAllDependencies(assetInfo.AssetPath);
List<BundleInfo> result = new List<BundleInfo>(depends.Length);
foreach (var packageBundle in depends)
{
BundleInfo bundleInfo = CreateBundleInfo(packageBundle);
result.Add(bundleInfo);
}
return result.ToArray();
}
string IBundleServices.GetBundleName(int bundleID)
{
return _activeManifest.GetBundleName(bundleID);
}
bool IBundleServices.IsServicesValid()
{
return _activeManifest != null;
}
#endregion
}
}

View File

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

View File

@ -126,6 +126,22 @@ namespace YooAsset
initializeParameters.RemoteServices initializeParameters.RemoteServices
); );
} }
else if (_playMode == EPlayMode.WebPlayMode)
{
var webPlayModeImpl = new WebPlayModeImpl();
_bundleServices = webPlayModeImpl;
_playModeServices = webPlayModeImpl;
_assetSystemImpl.Initialize(PackageName, false,
parameters.LoadingMaxTimeSlice, parameters.DownloadFailedTryAgain,
parameters.DecryptionServices, _bundleServices);
var initializeParameters = parameters as WebPlayModeParameters;
initializeOperation = webPlayModeImpl.InitializeAsync(
PackageName,
initializeParameters.QueryServices,
initializeParameters.RemoteServices
);
}
else else
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -184,9 +200,19 @@ namespace YooAsset
_playMode = EPlayMode.OfflinePlayMode; _playMode = EPlayMode.OfflinePlayMode;
else if (parameters is HostPlayModeParameters) else if (parameters is HostPlayModeParameters)
_playMode = EPlayMode.HostPlayMode; _playMode = EPlayMode.HostPlayMode;
else if (parameters is WebPlayModeParameters)
_playMode = EPlayMode.WebPlayMode;
else else
throw new NotImplementedException(); throw new NotImplementedException();
// 检测运行平台
if (_playMode == EPlayMode.HostPlayMode || _playMode == EPlayMode.OfflinePlayMode)
{
#if UNITY_WEBGL
throw new Exception($"WebGL plateform not support : {_playMode} ! Please use {nameof(EPlayMode.WebPlayMode)}");
#endif
}
// 检测参数范围 // 检测参数范围
if (parameters.LoadingMaxTimeSlice < 10) if (parameters.LoadingMaxTimeSlice < 10)
{ {

View File

@ -24,7 +24,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 清单文件格式版本 /// 清单文件格式版本
/// </summary> /// </summary>
public const string ManifestFileVersion = "1.4.17"; public const string ManifestFileVersion = "1.5.2";
/// <summary> /// <summary>