Update PatchSystem

HostPlayMode支持WebGL平台。
pull/35/head
hevinci 2022-08-05 16:24:10 +08:00
parent 2ff01d10b0
commit a6fdb5691a
4 changed files with 298 additions and 155 deletions

View File

@ -0,0 +1,257 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace YooAsset
{
/// <summary>
/// 本地缓存文件验证器
/// </summary>
internal abstract class PatchCacheVerifier
{
public abstract bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate);
public abstract bool UpdateVerifier();
public abstract float GetVerifierProgress();
public int VerifySuccessCount { protected set; get; } = 0;
public int VerifyFailCount { protected set; get; } = 0;
}
/// <summary>
/// 本地缓存文件验证器(线程版)
/// </summary>
internal class PatchCacheVerifierWithThread : PatchCacheVerifier
{
private class ThreadInfo
{
public bool Result = false;
public string FilePath { private set; get; }
public PatchBundle Bundle { private set; get; }
public ThreadInfo(string filePath, PatchBundle bundle)
{
FilePath = filePath;
Bundle = bundle;
}
}
private readonly ThreadSyncContext _syncContext = new ThreadSyncContext();
private readonly List<PatchBundle> _waitingList = new List<PatchBundle>(1000);
private readonly List<PatchBundle> _verifyingList = new List<PatchBundle>(100);
private int _verifyMaxNum;
private int _verifyTotalCount;
public override bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate)
{
// 遍历所有文件然后验证并缓存合法文件
foreach (var patchBundle in localPatchManifest.BundleList)
{
// 忽略缓存文件
if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash))
continue;
// 忽略APP资源
// 注意如果是APP资源并且哈希值相同则不需要下载
if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle))
{
if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash)
continue;
}
// 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性
if (weaklyUpdate)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
else
return false;
}
else
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
}
}
// 设置同时验证的最大数
ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads);
YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}");
_verifyMaxNum = Math.Min(workerThreads, ioThreads);
_verifyTotalCount = _waitingList.Count;
if (_verifyMaxNum < 1)
_verifyMaxNum = 1;
return true;
}
public override bool UpdateVerifier()
{
_syncContext.Update();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
return true;
if (_verifyingList.Count >= _verifyMaxNum)
return false;
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (_verifyingList.Count >= _verifyMaxNum)
break;
var patchBundle = _waitingList[i];
if (VerifyFile(patchBundle))
{
_waitingList.RemoveAt(i);
_verifyingList.Add(patchBundle);
}
else
{
YooLogger.Warning("The thread pool is failed queued.");
break;
}
}
return false;
}
public override float GetVerifierProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount;
}
private bool VerifyFile(PatchBundle patchBundle)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
ThreadInfo info = new ThreadInfo(filePath, patchBundle);
return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), info);
}
private void VerifyInThread(object infoObj)
{
ThreadInfo info = (ThreadInfo)infoObj;
info.Result = DownloadSystem.CheckContentIntegrity(info.FilePath, info.Bundle.FileSize, info.Bundle.FileCRC);
_syncContext.Post(VerifyCallback, info);
}
private void VerifyCallback(object obj)
{
ThreadInfo info = (ThreadInfo)obj;
if (info.Result)
{
VerifySuccessCount++;
DownloadSystem.CacheVerifyFile(info.Bundle.FileHash, info.Bundle.FileName);
}
else
{
VerifyFailCount++;
// NOTE不期望删除断点续传的资源文件
/*
YooLogger.Warning($"Failed to verify file : {info.FilePath}");
if (File.Exists(info.FilePath))
File.Delete(info.FilePath);
*/
}
_verifyingList.Remove(info.Bundle);
}
}
/// <summary>
/// 本地缓存文件验证器(非线程版)
/// </summary>
internal class PatchCacheVerifierWithoutThread : PatchCacheVerifier
{
private readonly List<PatchBundle> _waitingList = new List<PatchBundle>(1000);
private readonly List<PatchBundle> _verifyingList = new List<PatchBundle>(100);
private int _verifyMaxNum;
private int _verifyTotalCount;
public override bool InitVerifier(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate)
{
// 遍历所有文件然后验证并缓存合法文件
foreach (var patchBundle in localPatchManifest.BundleList)
{
// 忽略缓存文件
if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash))
continue;
// 忽略APP资源
// 注意如果是APP资源并且哈希值相同则不需要下载
if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle))
{
if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash)
continue;
}
// 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性
if (weaklyUpdate)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
else
return false;
}
else
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
}
}
// 设置同时验证的最大数
_verifyMaxNum = 32;
_verifyTotalCount = _waitingList.Count;
return true;
}
public override bool UpdateVerifier()
{
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
return true;
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (_verifyingList.Count >= _verifyMaxNum)
break;
var patchBundle = _waitingList[i];
VerifyFile(patchBundle);
_waitingList.RemoveAt(i);
_verifyingList.Add(patchBundle);
}
_verifyingList.Clear();
return false;
}
public override float GetVerifierProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount;
}
private void VerifyFile(PatchBundle patchBundle)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
bool result = DownloadSystem.CheckContentIntegrity(filePath, patchBundle.FileSize, patchBundle.FileCRC);
if (result)
{
VerifySuccessCount++;
DownloadSystem.CacheVerifyFile(patchBundle.FileHash, patchBundle.FileName);
}
else
{
VerifyFailCount++;
// NOTE不期望删除断点续传的资源文件
/*
YooLogger.Warning($"Failed to verify file : {info.FilePath}");
if (File.Exists(info.FilePath))
File.Delete(info.FilePath);
*/
}
}
}
}

View File

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

View File

@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace YooAsset
{
@ -69,7 +68,7 @@ namespace YooAsset
private ESteps _steps = ESteps.None;
private UnityWebDataRequester _downloader1;
private UnityWebDataRequester _downloader2;
private VerifyManager _verifyManager = new VerifyManager();
private PatchCacheVerifier _patchCacheVerifier;
private float _verifyTime;
internal HostPlayModeUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion, int timeout)
@ -77,6 +76,12 @@ namespace YooAsset
_impl = impl;
_resourceVersion = resourceVersion;
_timeout = timeout;
#if UNITY_WEBGL
_patchCacheVerifier = new PatchCacheVerifierWithoutThread();
#else
_patchCacheVerifier = new PatchCacheVerifierWithThread();
#endif
}
internal override void Start()
{
@ -172,20 +177,20 @@ namespace YooAsset
if (_steps == ESteps.InitVerifyingCache)
{
_verifyManager.InitVerifyingCache(_impl.AppPatchManifest, _impl.LocalPatchManifest, false);
_patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, false);
_verifyTime = UnityEngine.Time.realtimeSinceStartup;
_steps = ESteps.UpdateVerifyingCache;
}
if (_steps == ESteps.UpdateVerifyingCache)
{
Progress = _verifyManager.GetVerifyProgress();
if (_verifyManager.UpdateVerifyingCache())
Progress = _patchCacheVerifier.GetVerifierProgress();
if (_patchCacheVerifier.UpdateVerifier())
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime;
YooLogger.Log($"Verify result : Success {_verifyManager.VerifySuccessCount}, Fail {_verifyManager.VerifyFailCount}, Elapsed time {costTime} seconds");
YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds");
}
}
}
@ -268,13 +273,19 @@ namespace YooAsset
private readonly HostPlayModeImpl _impl;
private readonly int _resourceVersion;
private ESteps _steps = ESteps.None;
private VerifyManager _verifyManager = new VerifyManager();
private PatchCacheVerifier _patchCacheVerifier;
private float _verifyTime;
internal HostPlayModeWeaklyUpdateManifestOperation(HostPlayModeImpl impl, int resourceVersion)
{
_impl = impl;
_resourceVersion = resourceVersion;
#if UNITY_WEBGL
_patchCacheVerifier = new PatchCacheVerifierWithoutThread();
#else
_patchCacheVerifier = new PatchCacheVerifierWithThread();
#endif
}
internal override void Start()
{
@ -293,7 +304,7 @@ namespace YooAsset
if (_steps == ESteps.InitVerifyingCache)
{
if (_verifyManager.InitVerifyingCache(_impl.AppPatchManifest, _impl.LocalPatchManifest, true))
if (_patchCacheVerifier.InitVerifier(_impl.AppPatchManifest, _impl.LocalPatchManifest, true))
{
_verifyTime = UnityEngine.Time.realtimeSinceStartup;
_steps = ESteps.UpdateVerifyingCache;
@ -308,12 +319,12 @@ namespace YooAsset
if (_steps == ESteps.UpdateVerifyingCache)
{
Progress = _verifyManager.GetVerifyProgress();
if (_verifyManager.UpdateVerifyingCache())
Progress = _patchCacheVerifier.GetVerifierProgress();
if (_patchCacheVerifier.UpdateVerifier())
{
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyTime;
YooLogger.Log($"Verify result : Success {_verifyManager.VerifySuccessCount}, Fail {_verifyManager.VerifyFailCount}, Elapsed time {costTime} seconds");
if (_verifyManager.VerifyFailCount > 0)
YooLogger.Log($"Verify result : Success {_patchCacheVerifier.VerifySuccessCount}, Fail {_patchCacheVerifier.VerifyFailCount}, Elapsed time {costTime} seconds");
if (_patchCacheVerifier.VerifyFailCount > 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
@ -344,143 +355,4 @@ namespace YooAsset
}
}
}
/// <summary>
/// 本地缓存文件验证管理器
/// </summary>
internal class VerifyManager
{
private class ThreadInfo
{
public bool Result = false;
public string FilePath { private set; get; }
public PatchBundle Bundle { private set; get; }
public ThreadInfo(string filePath, PatchBundle bundle)
{
FilePath = filePath;
Bundle = bundle;
}
}
private readonly List<PatchBundle> _waitingList = new List<PatchBundle>(1000);
private readonly List<PatchBundle> _verifyingList = new List<PatchBundle>(100);
private readonly ThreadSyncContext _syncContext = new ThreadSyncContext();
private int _verifyMaxNum = 32;
private int _verifyTotalCount = 0;
public int VerifySuccessCount { private set; get; } = 0;
public int VerifyFailCount { private set; get; } = 0;
public bool InitVerifyingCache(PatchManifest appPatchManifest, PatchManifest localPatchManifest, bool weaklyUpdate)
{
// 遍历所有文件然后验证并缓存合法文件
foreach (var patchBundle in localPatchManifest.BundleList)
{
// 忽略缓存文件
if (DownloadSystem.ContainsVerifyFile(patchBundle.FileHash))
continue;
// 忽略APP资源
// 注意如果是APP资源并且哈希值相同则不需要下载
if (appPatchManifest.TryGetPatchBundle(patchBundle.BundleName, out PatchBundle appPatchBundle))
{
if (appPatchBundle.IsBuildin && appPatchBundle.FileHash == patchBundle.FileHash)
continue;
}
// 注意:在弱联网模式下,我们需要验证指定资源版本的所有资源完整性
if (weaklyUpdate)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
else
return false;
}
else
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
if (File.Exists(filePath))
_waitingList.Add(patchBundle);
}
}
// 设置同时验证的最大数
ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads);
YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}");
_verifyMaxNum = Math.Min(workerThreads, ioThreads);
_verifyTotalCount = _waitingList.Count;
return true;
}
public bool UpdateVerifyingCache()
{
_syncContext.Update();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
return true;
if (_verifyingList.Count >= _verifyMaxNum)
return false;
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (_verifyingList.Count >= _verifyMaxNum)
break;
var patchBundle = _waitingList[i];
if (RunThread(patchBundle))
{
_waitingList.RemoveAt(i);
_verifyingList.Add(patchBundle);
}
else
{
YooLogger.Warning("The thread pool is failed queued.");
break;
}
}
return false;
}
public float GetVerifyProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(VerifySuccessCount + VerifyFailCount) / _verifyTotalCount;
}
private bool RunThread(PatchBundle patchBundle)
{
string filePath = SandboxHelper.MakeCacheFilePath(patchBundle.FileName);
ThreadInfo info = new ThreadInfo(filePath, patchBundle);
return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), info);
}
private void VerifyInThread(object infoObj)
{
ThreadInfo info = (ThreadInfo)infoObj;
info.Result = DownloadSystem.CheckContentIntegrity(info.FilePath, info.Bundle.FileSize, info.Bundle.FileCRC);
_syncContext.Post(VerifyCallback, info);
}
private void VerifyCallback(object obj)
{
ThreadInfo info = (ThreadInfo)obj;
if (info.Result)
{
VerifySuccessCount++;
DownloadSystem.CacheVerifyFile(info.Bundle.FileHash, info.Bundle.FileName);
}
else
{
VerifyFailCount++;
// NOTE不期望删除断点续传的资源文件
/*
YooLogger.Warning($"Failed to verify file : {info.FilePath}");
if (File.Exists(info.FilePath))
File.Delete(info.FilePath);
*/
}
_verifyingList.Remove(info.Bundle);
}
}
}

View File

@ -100,10 +100,17 @@ namespace YooAsset
/// </summary>
public bool ClearCacheWhenDirty = false;
#if UNITY_WEBGL
/// <summary>
/// WEBGL模式不支持多线程下载
/// </summary>
internal int BreakpointResumeFileSize = int.MaxValue;
#else
/// <summary>
/// 启用断点续传功能的文件大小
/// </summary>
public int BreakpointResumeFileSize = int.MaxValue;
#endif
/// <summary>
/// 下载文件校验等级
@ -194,12 +201,8 @@ namespace YooAsset
// 初始化下载系统
if (_playMode == EPlayMode.HostPlayMode)
{
#if UNITY_WEBGL
throw new Exception($"{EPlayMode.HostPlayMode} not supports WebGL platform !");
#else
var hostPlayModeParameters = parameters as HostPlayModeParameters;
DownloadSystem.Initialize(hostPlayModeParameters.BreakpointResumeFileSize, hostPlayModeParameters.VerifyLevel);
#endif
}
// 初始化资源系统