update new cache system

重新设计了资源缓存系统
pull/62/head
hevinci 2023-02-07 18:39:08 +08:00
parent 33a1b9d4bf
commit 8ce8e81792
38 changed files with 858 additions and 841 deletions

View File

@ -199,7 +199,7 @@ namespace YooAsset.Editor
{
if (IsRawAsset)
{
string mainBundleName = $"{_mainBundleName}.{YooAssetSettingsData.Setting.RawFileVariant}";
string mainBundleName = $"{_mainBundleName}.{YooAssetSettingsData.Setting.RawBundleFileVariant}";
_mainBundleName = mainBundleName.ToLower();
}
else

View File

@ -46,7 +46,7 @@ namespace YooAsset
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote)
{
_steps = ESteps.Download;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming)
{
@ -55,7 +55,7 @@ namespace YooAsset
if (loadMethod == EBundleLoadMethod.LoadFromMemory || loadMethod == EBundleLoadMethod.LoadFromStream)
{
_steps = ESteps.Unpack;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else
{
@ -70,7 +70,7 @@ namespace YooAsset
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
_steps = ESteps.LoadFile;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else
{
@ -242,14 +242,11 @@ namespace YooAsset
// 在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
string cacheLoadPath = MainBundleInfo.Bundle.CachedFilePath;
if (CacheSystem.VerifyBundle(MainBundleInfo.Bundle, EVerifyLevel.High) != EVerifyResult.Succeed)
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
if (result != EVerifyResult.Succeed)
{
if (File.Exists(cacheLoadPath))
{
YooLogger.Error($"Delete the invalid cache file : {cacheLoadPath}");
File.Delete(cacheLoadPath);
}
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}");
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
}
}
}

View File

@ -47,7 +47,7 @@ namespace YooAsset
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote)
{
_steps = ESteps.Download;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming)
{
@ -57,7 +57,7 @@ namespace YooAsset
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
_steps = ESteps.LoadCacheFile;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else
{
@ -147,14 +147,11 @@ namespace YooAsset
// 在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
string cacheLoadPath = MainBundleInfo.Bundle.CachedFilePath;
if (CacheSystem.VerifyBundle(MainBundleInfo.Bundle, EVerifyLevel.High) != EVerifyResult.Succeed)
var result = CacheSystem.VerifyingRecordFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
if (result != EVerifyResult.Succeed)
{
if (File.Exists(cacheLoadPath))
{
YooLogger.Error($"Delete the invalid cache file : {cacheLoadPath}");
File.Delete(cacheLoadPath);
}
YooLogger.Error($"Found possibly corrupt file ! {MainBundleInfo.Bundle.CacheGUID}");
CacheSystem.DiscardFile(MainBundleInfo.Bundle.PackageName, MainBundleInfo.Bundle.CacheGUID);
}
}
}

View File

@ -38,13 +38,13 @@ namespace YooAsset
if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromRemote)
{
_steps = ESteps.Download;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromStreaming)
{
#if UNITY_ANDROID || UNITY_WEBGL
_steps = ESteps.Unpack;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
#else
_steps = ESteps.CheckFile;
FileLoadPath = MainBundleInfo.Bundle.StreamingFilePath;
@ -53,7 +53,7 @@ namespace YooAsset
else if (MainBundleInfo.LoadMode == BundleInfo.ELoadMode.LoadFromCache)
{
_steps = ESteps.CheckFile;
FileLoadPath = MainBundleInfo.Bundle.CachedFilePath;
FileLoadPath = MainBundleInfo.Bundle.CachedDataFilePath;
}
else
{

View File

@ -229,15 +229,6 @@ namespace YooAsset
return _playModeServices.PreDownloadPackageAsync(packageVersion, timeout);
}
/// <summary>
/// 检查包裹内容的完整性
/// </summary>
public CheckPackageContentsOperation CheckPackageContentsAsync(string packageVersion)
{
DebugCheckInitialize();
return _playModeServices.CheckPackageContentsOperation(packageVersion);
}
/// <summary>
/// 清理包裹未使用的缓存文件
/// </summary>
@ -731,12 +722,12 @@ namespace YooAsset
/// <summary>
/// 是否包含资源文件
/// </summary>
internal bool IsIncludeBundleFile(string fileName)
internal bool IsIncludeBundleFile(string cacheGUID)
{
// NOTE : 编辑器模拟模式下始终返回TRUE
if (_playMode == EPlayMode.EditorSimulateMode)
return true;
return _playModeServices.ActiveManifest.IsIncludeBundleFile(fileName);
return _playModeServices.ActiveManifest.IsIncludeBundleFile(cacheGUID);
}
/// <summary>

View File

@ -0,0 +1,12 @@
using System;
using System.IO;
namespace YooAsset
{
[Serializable]
internal class CacheFileInfo
{
public string FileCRC;
public long FileSize;
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a6296859f09655c4191594304ddf378f
guid: e4a97c06e069c1146a881fcb359f9b4b
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -2,19 +2,19 @@
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace YooAsset
{
internal static class CacheSystem
{
private readonly static Dictionary<string, PatchBundle> _cachedDic = new Dictionary<string, PatchBundle>(1000);
private readonly static Dictionary<string, PackageCache> _cachedDic = new Dictionary<string, PackageCache>(1000);
/// <summary>
/// 初始化时的验证级别
/// </summary>
public static EVerifyLevel InitVerifyLevel { set; get; } = EVerifyLevel.Low;
/// <summary>
/// 清空所有数据
/// </summary>
@ -25,103 +25,119 @@ namespace YooAsset
/// <summary>
/// 查询是否为验证文件
/// 注意:被收录的文件完整性是绝对有效的
/// </summary>
public static bool IsCached(PatchBundle patchBundle)
public static bool IsCached(string packageName, string cacheGUID)
{
string cacheKey = patchBundle.CacheKey;
if (_cachedDic.ContainsKey(cacheKey))
var cache = GetOrCreateCache(packageName);
return cache.IsCached(cacheGUID);
}
/// <summary>
/// 录入验证的文件
/// </summary>
public static void RecordFile(string packageName, string cacheGUID, PackageCache.RecordWrapper wrapper)
{
var cache = GetOrCreateCache(packageName);
cache.Record(cacheGUID, wrapper);
}
/// <summary>
/// 丢弃验证的文件(同时删除文件)
/// </summary>
public static void DiscardFile(string packageName, string cacheGUID)
{
var cache = GetOrCreateCache(packageName);
var wrapper = cache.TryGetWrapper(cacheGUID);
if (wrapper == null)
return;
cache.Discard(cacheGUID);
try
{
string filePath = patchBundle.CachedFilePath;
if (File.Exists(filePath))
{
return true;
}
else
{
_cachedDic.Remove(cacheKey);
YooLogger.Error($"Cache file is missing : {filePath}");
return false;
}
string dataFilePath = wrapper.DataFilePath;
FileInfo fileInfo = new FileInfo(dataFilePath);
if (fileInfo.Exists)
fileInfo.Directory.Delete(true);
}
else
catch (Exception e)
{
return false;
YooLogger.Error($"Failed to delete cache file ! {e.Message}");
}
}
/// <summary>
/// 缓存补丁包文件
/// 验证缓存文件(子线程内操作)
/// </summary>
public static void CacheBundle(PatchBundle patchBundle)
public static EVerifyResult VerifyingCacheFile(VerifyElement element, EVerifyLevel verifyLevel)
{
string cacheKey = patchBundle.CacheKey;
if (_cachedDic.ContainsKey(cacheKey) == false)
try
{
string filePath = patchBundle.CachedFilePath;
YooLogger.Log($"Cache verify file : {filePath}");
_cachedDic.Add(cacheKey, patchBundle);
string infoFilePath = element.InfoFilePath;
if (File.Exists(infoFilePath) == false)
return EVerifyResult.InfoFileNotExisted;
// 解析信息文件获取验证数据
string jsonContent = FileUtility.ReadAllText(infoFilePath);
CacheFileInfo fileInfo = UnityEngine.JsonUtility.FromJson<CacheFileInfo>(jsonContent);
element.DataFileCRC = fileInfo.FileCRC;
element.DataFileSize = fileInfo.FileSize;
}
}
/// <summary>
/// 验证补丁包文件
/// </summary>
public static EVerifyResult VerifyBundle(PatchBundle patchBundle, EVerifyLevel verifyLevel)
{
return VerifyContentInternal(patchBundle.CachedFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel);
}
/// <summary>
/// 验证并缓存本地文件
/// </summary>
public static EVerifyResult VerifyAndCacheLocalBundleFile(PatchBundle patchBundle, EVerifyLevel verifyLevel)
{
var verifyResult = VerifyContentInternal(patchBundle.CachedFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel);
if (verifyResult == EVerifyResult.Succeed)
CacheBundle(patchBundle);
return verifyResult;
}
/// <summary>
/// 验证并缓存下载文件
/// </summary>
public static EVerifyResult VerifyAndCacheDownloadBundleFile(string tempFilePath, PatchBundle patchBundle, EVerifyLevel verifyLevel)
{
var verifyResult = VerifyContentInternal(tempFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel);
if (verifyResult == EVerifyResult.Succeed)
catch (Exception)
{
try
{
string destFilePath = patchBundle.CachedFilePath;
if (File.Exists(destFilePath))
File.Delete(destFilePath);
return EVerifyResult.Exception;
}
FileInfo fileInfo = new FileInfo(tempFilePath);
fileInfo.MoveTo(destFilePath);
}
catch (Exception)
{
verifyResult = EVerifyResult.FileMoveFailed;
}
return VerifyingInternal(element.DataFilePath, element.DataFileSize, element.DataFileCRC, verifyLevel);
}
if (verifyResult == EVerifyResult.Succeed)
/// <summary>
/// 验证下载文件
/// </summary>
public static EVerifyResult VerifyingTempFile(PatchBundle patchBundle, EVerifyLevel verifyLevel)
{
return VerifyingInternal(patchBundle.TempDataFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel);
}
/// <summary>
/// 验证记录文件
/// </summary>
public static EVerifyResult VerifyingRecordFile(string packageName, string cacheGUID)
{
var cache = GetOrCreateCache(packageName);
var wrapper = cache.TryGetWrapper(cacheGUID);
if (wrapper == null)
return EVerifyResult.CacheNotFound;
EVerifyResult result = VerifyingInternal(wrapper.DataFilePath, wrapper.DataFileSize, wrapper.DataFileCRC, EVerifyLevel.High);
return result;
}
/// <summary>
/// 获取未被使用的缓存文件
/// </summary>
public static List<string> GetUnusedCacheGUIDs(AssetsPackage package)
{
var cache = GetOrCreateCache(package.PackageName);
var keys = cache.GetAllKeys();
List<string> result = new List<string>(keys.Count);
foreach (var cacheGUID in keys)
{
if (package.IsIncludeBundleFile(cacheGUID) == false)
{
CacheBundle(patchBundle);
result.Add(cacheGUID);
}
}
return verifyResult;
return result;
}
/// <summary>
/// 验证文件完整性
/// </summary>
private static EVerifyResult VerifyContentInternal(string filePath, long fileSize, string fileCRC, EVerifyLevel verifyLevel)
private static EVerifyResult VerifyingInternal(string filePath, long fileSize, string fileCRC, EVerifyLevel verifyLevel)
{
try
{
if (File.Exists(filePath) == false)
return EVerifyResult.FileNotExisted;
return EVerifyResult.DataFileNotExisted;
// 先验证文件大小
long size = FileUtility.GetFileSize(filePath);
@ -149,5 +165,14 @@ namespace YooAsset
return EVerifyResult.Exception;
}
}
private static PackageCache GetOrCreateCache(string packageName)
{
if (_cachedDic.TryGetValue(packageName, out PackageCache cache) == false)
{
cache = new PackageCache(packageName);
_cachedDic.Add(packageName, cache);
}
return cache;
}
}
}

View File

@ -7,14 +7,19 @@ namespace YooAsset
internal enum EVerifyResult
{
/// <summary>
/// 文件不存在
/// 未找到缓存信息
/// </summary>
FileNotExisted = -5,
CacheNotFound = -6,
/// <summary>
/// 文件移动失败(重命名失败)
/// 信息文件不存在
/// </summary>
FileMoveFailed = -4,
InfoFileNotExisted = -5,
/// <summary>
/// 数据文件不存在
/// </summary>
DataFileNotExisted = -4,
/// <summary>
/// 文件内容不足(小于正常大小)

View File

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

View File

@ -0,0 +1,71 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace YooAsset
{
/// <summary>
/// 清理本地包裹未使用的缓存文件
/// </summary>
public sealed class ClearUnusedCacheFilesOperation : AsyncOperationBase
{
private enum ESteps
{
None,
GetUnusedCacheFiles,
ClearUnusedCacheFiles,
Done,
}
private readonly AssetsPackage _package;
private List<string> _unusedCacheGUIDs;
private int _unusedFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearUnusedCacheFilesOperation(AssetsPackage package)
{
_package = package;
}
internal override void Start()
{
_steps = ESteps.GetUnusedCacheFiles;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetUnusedCacheFiles)
{
_unusedCacheGUIDs = CacheSystem.GetUnusedCacheGUIDs(_package);
_unusedFileTotalCount = _unusedCacheGUIDs.Count;
YooLogger.Log($"Found unused cache file count : {_unusedFileTotalCount}");
_steps = ESteps.ClearUnusedCacheFiles;
}
if (_steps == ESteps.ClearUnusedCacheFiles)
{
for (int i = _unusedCacheGUIDs.Count - 1; i >= 0; i--)
{
string cacheGUID = _unusedCacheGUIDs[i];
CacheSystem.DiscardFile(_package.PackageName, cacheGUID);
_unusedCacheGUIDs.RemoveAt(i);
if (OperationSystem.IsBusy)
break;
}
if (_unusedFileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_unusedCacheGUIDs.Count / _unusedFileTotalCount);
if (_unusedCacheGUIDs.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

View File

@ -0,0 +1,80 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal class PackageCachingOperation : AsyncOperationBase
{
private enum ESteps
{
None,
GetCacheFiles,
VerifyCacheFiles,
Done,
}
private readonly string _packageName;
private PackageVerifyOperation _packageVerifyOp;
private ESteps _steps = ESteps.None;
public PackageCachingOperation(string packageName)
{
_packageName = packageName;
}
internal override void Start()
{
_steps = ESteps.GetCacheFiles;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetCacheFiles)
{
var elements = GetVerifyElements();
_packageVerifyOp = PackageVerifyOperation.CreateOperation(elements);
OperationSystem.StartOperation(_packageVerifyOp);
_steps = ESteps.VerifyCacheFiles;
}
if (_steps == ESteps.VerifyCacheFiles)
{
Progress = _packageVerifyOp.Progress;
if (_packageVerifyOp.IsDone == false)
return;
// 注意:总是返回成功
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
private List<VerifyElement> GetVerifyElements()
{
string cacheFolderPath = PersistentHelper.GetCacheFolderPath(_packageName);
if (Directory.Exists(cacheFolderPath) == false)
return new List<VerifyElement>();
DirectoryInfo rootDirectory = new DirectoryInfo(cacheFolderPath);
DirectoryInfo[] fileFolders = rootDirectory.GetDirectories();
List<VerifyElement> result = new List<VerifyElement>(fileFolders.Length);
foreach (var fileFoder in fileFolders)
{
string cacheGUID = fileFoder.Name;
if (CacheSystem.IsCached(_packageName, cacheGUID))
continue;
string fileRootPath = fileFoder.FullName;
string dataFilePath = $"{fileRootPath}/{ YooAssetSettings.CacheBundleDataFileName}";
string infoFilePath = $"{fileRootPath}/{ YooAssetSettings.CacheBundleInfoFileName}";
VerifyElement element = new VerifyElement(_packageName, cacheGUID, fileRootPath, dataFilePath, infoFilePath);
result.Add(element);
}
return result;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 3d400b2548d79ca42bec7370f5d66b78
guid: 4ff95e7516dbfa148b4fe16eaab783fb
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,258 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace YooAsset
{
internal abstract class PackageVerifyOperation : AsyncOperationBase
{
public static PackageVerifyOperation CreateOperation(List<VerifyElement> elements)
{
#if UNITY_WEBGL
var operation = new PackageVerifyWithoutThreadOperation(elements);
#else
var operation = new PackageVerifyWithThreadOperation(elements);
#endif
return operation;
}
}
/// <summary>
/// 本地缓存文件验证(线程版)
/// </summary>
internal class PackageVerifyWithThreadOperation : PackageVerifyOperation
{
private enum ESteps
{
None,
InitVerify,
UpdateVerify,
Done,
}
private readonly ThreadSyncContext _syncContext = new ThreadSyncContext();
private List<VerifyElement> _waitingList;
private List<VerifyElement> _verifyingList;
private int _verifyMaxNum;
private int _verifyTotalCount;
private float _verifyStartTime;
private int _succeedCount;
private int _failedCount;
private ESteps _steps = ESteps.None;
public PackageVerifyWithThreadOperation(List<VerifyElement> elements)
{
_waitingList = elements;
}
internal override void Start()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.InitVerify)
{
int fileCount = _waitingList.Count;
// 设置同时验证的最大数
ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads);
YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}");
_verifyMaxNum = Math.Min(workerThreads, ioThreads);
_verifyTotalCount = fileCount;
if (_verifyMaxNum < 1)
_verifyMaxNum = 1;
_verifyingList = new List<VerifyElement>(_verifyMaxNum);
_steps = ESteps.UpdateVerify;
}
if (_steps == ESteps.UpdateVerify)
{
_syncContext.Update();
Progress = GetProgress();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Package verify elapsed time {costTime:f1} seconds");
}
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (OperationSystem.IsBusy)
break;
if (_verifyingList.Count >= _verifyMaxNum)
break;
var element = _waitingList[i];
if (BeginVerifyFileWithThread(element))
{
_waitingList.RemoveAt(i);
_verifyingList.Add(element);
}
else
{
YooLogger.Warning("The thread pool is failed queued.");
break;
}
}
}
}
private float GetProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(_succeedCount + _failedCount) / _verifyTotalCount;
}
private bool BeginVerifyFileWithThread(VerifyElement element)
{
return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), element);
}
private void VerifyInThread(object obj)
{
VerifyElement element = (VerifyElement)obj;
element.Result = CacheSystem.VerifyingCacheFile(element, CacheSystem.InitVerifyLevel);
_syncContext.Post(VerifyCallback, element);
}
private void VerifyCallback(object obj)
{
VerifyElement element = (VerifyElement)obj;
_verifyingList.Remove(element);
if (element.Result == EVerifyResult.Succeed)
{
_succeedCount++;
var wrapper = new PackageCache.RecordWrapper(element.InfoFilePath, element.DataFilePath, element.DataFileCRC, element.DataFileSize);
CacheSystem.RecordFile(element.PackageName, element.CacheGUID, wrapper);
}
else
{
_failedCount++;
// 删除验证失败的缓存文件
if (Directory.Exists(element.FileRootPath))
{
YooLogger.Warning($"Failed verify file and delete files : {element.FileRootPath}");
Directory.Delete(element.FileRootPath, true);
}
}
}
}
/// <summary>
/// 本地缓存文件验证(非线程版)
/// </summary>
internal class PackageVerifyWithoutThreadOperation : PackageVerifyOperation
{
private enum ESteps
{
None,
InitVerify,
UpdateVerify,
Done,
}
private List<VerifyElement> _waitingList;
private List<VerifyElement> _verifyingList;
private int _verifyMaxNum;
private int _verifyTotalCount;
private float _verifyStartTime;
private int _succeedCount;
private int _failedCount;
private ESteps _steps = ESteps.None;
public PackageVerifyWithoutThreadOperation(List<VerifyElement> elements)
{
_waitingList = elements;
}
internal override void Start()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.InitVerify)
{
int fileCount = _waitingList.Count;
// 设置同时验证的最大数
_verifyMaxNum = fileCount;
_verifyTotalCount = fileCount;
_verifyingList = new List<VerifyElement>(_verifyMaxNum);
_steps = ESteps.UpdateVerify;
}
if (_steps == ESteps.UpdateVerify)
{
Progress = GetProgress();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Package verify elapsed time {costTime:f1} seconds");
}
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (OperationSystem.IsBusy)
break;
if (_verifyingList.Count >= _verifyMaxNum)
break;
var element = _waitingList[i];
BeginVerifyFileWithoutThread(element);
_waitingList.RemoveAt(i);
_verifyingList.Add(element);
}
// 主线程内验证,可以清空列表
_verifyingList.Clear();
}
}
private float GetProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(_succeedCount + _failedCount) / _verifyTotalCount;
}
private void BeginVerifyFileWithoutThread(VerifyElement element)
{
element.Result = CacheSystem.VerifyingCacheFile(element, CacheSystem.InitVerifyLevel);
if (element.Result == EVerifyResult.Succeed)
{
_succeedCount++;
var wrapper = new PackageCache.RecordWrapper(element.InfoFilePath, element.DataFilePath, element.DataFileCRC, element.DataFileSize);
CacheSystem.RecordFile(element.PackageName, element.CacheGUID, wrapper);
}
else
{
_failedCount++;
// 删除验证失败的缓存文件
if (Directory.Exists(element.FileRootPath))
{
YooLogger.Warning($"Failed verify file and delete files : {element.FileRootPath}");
Directory.Delete(element.FileRootPath, true);
}
}
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
namespace YooAsset
{
internal class PackageCache
{
internal class RecordWrapper
{
public string InfoFilePath { private set; get; }
public string DataFilePath { private set; get; }
public string DataFileCRC { private set; get; }
public long DataFileSize { private set; get; }
public RecordWrapper(string infoFilePath, string dataFilePath, string dataFileCRC, long dataFileSize)
{
InfoFilePath = infoFilePath;
DataFilePath = dataFilePath;
DataFileCRC = dataFileCRC;
DataFileSize = dataFileSize;
}
}
private readonly Dictionary<string, RecordWrapper> _wrappers = new Dictionary<string, RecordWrapper>();
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
public PackageCache(string packageName)
{
PackageName = packageName;
}
/// <summary>
/// 清空所有数据
/// </summary>
public void ClearAll()
{
_wrappers.Clear();
}
/// <summary>
/// 查询缓存记录
/// </summary>
public bool IsCached(string cacheGUID)
{
return _wrappers.ContainsKey(cacheGUID);
}
/// <summary>
/// 记录验证结果
/// </summary>
public void Record(string cacheGUID, RecordWrapper wrapper)
{
if (_wrappers.ContainsKey(cacheGUID) == false)
{
_wrappers.Add(cacheGUID, wrapper);
}
else
{
throw new Exception("Should never get here !");
}
}
/// <summary>
/// 丢弃验证结果
/// </summary>
public void Discard(string cacheGUID)
{
if (_wrappers.ContainsKey(cacheGUID))
{
_wrappers.Remove(cacheGUID);
}
}
/// <summary>
/// 获取记录对象
/// </summary>
public RecordWrapper TryGetWrapper(string cacheGUID)
{
if (_wrappers.TryGetValue(cacheGUID, out RecordWrapper value))
return value;
else
return null;
}
internal List<string> GetAllKeys()
{
List<string> keys = new List<string>(_wrappers.Keys.Count);
var keyCollection = _wrappers.Keys;
foreach (var key in keyCollection)
{
keys.Add(key);
}
return keys;
}
}
}

View File

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

View File

@ -0,0 +1,25 @@

namespace YooAsset
{
internal class VerifyElement
{
public string PackageName { private set; get; }
public string CacheGUID { private set; get; }
public string FileRootPath { private set; get; }
public string DataFilePath { private set; get; }
public string InfoFilePath { private set; get; }
public EVerifyResult Result;
public string DataFileCRC;
public long DataFileSize;
public VerifyElement(string packageName, string cacheGUID, string fileRootPath, string dataFilePath, string infoFilePath)
{
PackageName = packageName;
CacheGUID = cacheGUID;
FileRootPath = fileRootPath;
DataFilePath = dataFilePath;
InfoFilePath = infoFilePath;
}
}
}

View File

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

View File

@ -1,33 +0,0 @@

namespace YooAsset
{
internal class VerifyInfo
{
/// <summary>
/// 验证的资源文件是否为内置资源
/// </summary>
public bool IsBuildinFile { private set; get; }
/// <summary>
/// 验证的资源包实例
/// </summary>
public PatchBundle VerifyBundle { private set; get; }
/// <summary>
/// 验证的文件路径
/// </summary>
public string VerifyFilePath { private set; get; }
/// <summary>
/// 验证结果
/// </summary>
public EVerifyResult Result;
public VerifyInfo(bool isBuildinFile, PatchBundle verifyBundle)
{
IsBuildinFile = isBuildinFile;
VerifyBundle = verifyBundle;
VerifyFilePath = verifyBundle.CachedFilePath;
}
}
}

View File

@ -77,13 +77,13 @@ namespace YooAsset
public static DownloaderBase BeginDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60)
{
// 查询存在的下载器
if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedFilePath, out var downloader))
if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedDataFilePath, out var downloader))
{
return downloader;
}
// 如果资源已经缓存
if (CacheSystem.IsCached(bundleInfo.Bundle))
if (CacheSystem.IsCached(bundleInfo.Bundle.PackageName, bundleInfo.Bundle.CacheGUID))
{
var tempDownloader = new TempDownloader(bundleInfo);
return tempDownloader;
@ -92,11 +92,11 @@ namespace YooAsset
// 创建新的下载器
{
YooLogger.Log($"Beginning to download file : {bundleInfo.Bundle.FileName} URL : {bundleInfo.RemoteMainURL}");
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedFilePath);
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath);
bool breakDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize;
DownloaderBase newDownloader = new FileDownloader(bundleInfo, breakDownload);
newDownloader.SendRequest(failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedFilePath, newDownloader);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
}
}

View File

@ -6,13 +6,13 @@ namespace YooAsset
protected enum ESteps
{
None,
CheckLocalFile,
CheckTempFile,
PrepareDownload,
CreateResumeDownloader,
CreateGeneralDownloader,
CheckDownload,
VerifyDownload,
VerifyingFile,
CachingFile,
TryAgain,
Succeed,
Failed,
@ -60,7 +60,7 @@ namespace YooAsset
{
_failedTryAgain = failedTryAgain;
_timeout = timeout;
_steps = ESteps.CheckLocalFile;
_steps = ESteps.CheckTempFile;
}
}
public abstract void Update();

View File

@ -25,7 +25,7 @@ namespace YooAsset
public FileDownloader(BundleInfo bundleInfo, bool breakResume) : base(bundleInfo)
{
_breakResume = breakResume;
_tempFilePath = bundleInfo.Bundle.CachedFilePath + ".temp";
_tempFilePath = bundleInfo.Bundle.TempDataFilePath;
}
public override void Update()
{
@ -34,29 +34,13 @@ namespace YooAsset
if (IsDone())
return;
// 检测本地正式文件
if (_steps == ESteps.CheckLocalFile)
{
var verifyResult = CacheSystem.VerifyAndCacheLocalBundleFile(_bundleInfo.Bundle, CacheSystem.InitVerifyLevel);
if (verifyResult == EVerifyResult.Succeed)
{
_steps = ESteps.Succeed;
}
else
{
if (File.Exists(_bundleInfo.Bundle.CachedFilePath))
File.Delete(_bundleInfo.Bundle.CachedFilePath);
_steps = ESteps.CheckTempFile;
}
}
// 检测本地临时文件
if (_steps == ESteps.CheckTempFile)
{
var verifyResult = CacheSystem.VerifyAndCacheDownloadBundleFile(_tempFilePath, _bundleInfo.Bundle, EVerifyLevel.High);
var verifyResult = CacheSystem.VerifyingTempFile(_bundleInfo.Bundle, EVerifyLevel.High);
if (verifyResult == EVerifyResult.Succeed)
{
_steps = ESteps.Succeed;
_steps = ESteps.CachingFile;
}
else
{
@ -94,9 +78,7 @@ namespace YooAsset
if (_steps == ESteps.CreateGeneralDownloader)
{
if (File.Exists(_tempFilePath))
{
File.Delete(_tempFilePath);
}
_webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
@ -206,7 +188,7 @@ namespace YooAsset
}
else
{
_steps = ESteps.VerifyDownload;
_steps = ESteps.VerifyingFile;
}
// 释放下载器
@ -214,23 +196,55 @@ namespace YooAsset
}
// 验证下载文件
if (_steps == ESteps.VerifyDownload)
if (_steps == ESteps.VerifyingFile)
{
var verifyResult = CacheSystem.VerifyAndCacheDownloadBundleFile(_tempFilePath, _bundleInfo.Bundle, EVerifyLevel.High);
var verifyResult = CacheSystem.VerifyingTempFile(_bundleInfo.Bundle, EVerifyLevel.High);
if (verifyResult == EVerifyResult.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
_lastError = $"Failed to verifying file : {_bundleInfo.Bundle.FileName}, ErrorCode : {verifyResult}";
// 注意:验证失败后删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.TryAgain;
}
}
// 缓存下载文件
if (_steps == ESteps.CachingFile)
{
try
{
string destFilePath = _bundleInfo.Bundle.CachedDataFilePath;
if (File.Exists(destFilePath))
File.Delete(destFilePath);
FileInfo fileInfo = new FileInfo(_tempFilePath);
fileInfo.MoveTo(destFilePath);
// 写入信息文件记录验证数据
CacheFileInfo cacheInfo = new CacheFileInfo();
cacheInfo.FileCRC = _bundleInfo.Bundle.FileCRC;
cacheInfo.FileSize = _bundleInfo.Bundle.FileSize;
string jsonContent = UnityEngine.JsonUtility.ToJson(cacheInfo);
FileUtility.CreateFile(_bundleInfo.Bundle.CachedInfoFilePath, jsonContent);
// 记录缓存文件
var wrapper = new PackageCache.RecordWrapper(_bundleInfo.Bundle.CachedInfoFilePath, _bundleInfo.Bundle.CachedDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper);
_lastError = string.Empty;
_lastCode = 0;
_steps = ESteps.Succeed;
}
else
catch (Exception e)
{
_lastError = $"Verify bundle content failed : {_bundleInfo.Bundle.FileName}";
// 验证失败后删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_lastError = e.Message;
_steps = ESteps.TryAgain;
}
}

View File

@ -1,150 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 检查包裹内容的完整性
/// </summary>
public abstract class CheckPackageContentsOperation : AsyncOperationBase
{
}
internal sealed class EditorSimulateModeCheckPackageContentsOperation : CheckPackageContentsOperation
{
internal EditorSimulateModeCheckPackageContentsOperation()
{
}
internal override void Start()
{
Status = EOperationStatus.Succeed;
}
internal override void Update()
{
}
}
internal sealed class OfflinePlayModeCheckPackageContentsOperation : CheckPackageContentsOperation
{
internal OfflinePlayModeCheckPackageContentsOperation()
{
}
internal override void Start()
{
Status = EOperationStatus.Succeed;
}
internal override void Update()
{
}
}
internal sealed class HostPlayModeCheckPackageContentsOperation : CheckPackageContentsOperation
{
private enum ESteps
{
None,
CheckActiveManifest,
LoadCacheManifest,
VerifyPackage,
Done,
}
private readonly HostPlayModeImpl _impl;
private readonly string _packageName;
private readonly string _packageVersion;
private LoadCacheManifestOperation _loadCacheManifestOp;
private VerifyPackageOperation _verifyOperation;
private PatchManifest _verifyManifest;
private ESteps _steps = ESteps.None;
internal HostPlayModeCheckPackageContentsOperation(HostPlayModeImpl impl, string packageName, string packageVersion)
{
_impl = impl;
_packageName = packageName;
_packageVersion = packageVersion;
}
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)
{
_verifyManifest = _impl.ActiveManifest;
_steps = ESteps.VerifyPackage;
}
else
{
_steps = ESteps.LoadCacheManifest;
}
}
if (_steps == ESteps.LoadCacheManifest)
{
if (_loadCacheManifestOp == null)
{
_loadCacheManifestOp = new LoadCacheManifestOperation(_packageName, _packageVersion);
OperationSystem.StartOperation(_loadCacheManifestOp);
}
if (_loadCacheManifestOp.IsDone == false)
return;
if (_loadCacheManifestOp.Status == EOperationStatus.Succeed)
{
_verifyManifest = _loadCacheManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _loadCacheManifestOp.Error;
}
}
if (_steps == ESteps.VerifyPackage)
{
if (_verifyOperation == null)
{
_verifyOperation = VerifyPackageOperation.CreateOperation(_verifyManifest, _impl);
OperationSystem.StartOperation(_verifyOperation);
}
Progress = _verifyOperation.Progress;
if (_verifyOperation.IsDone == false)
return;
bool verifySucceed = true;
foreach (var verifyInfo in _verifyOperation.VerifyFailList)
{
// 注意:跳过内置资源文件
if (verifyInfo.IsBuildinFile)
continue;
verifySucceed = false;
YooLogger.Warning($"Failed verify file : {verifyInfo.VerifyFilePath}");
}
if (verifySucceed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"The package resource {_packageName} content has verify failed file !";
}
}
}
}
}

View File

@ -1,104 +0,0 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace YooAsset
{
/// <summary>
/// 清理本地包裹未使用的缓存文件
/// </summary>
public sealed class ClearUnusedCacheFilesOperation : AsyncOperationBase
{
private enum ESteps
{
None,
GetUnusedCacheFiles,
ClearUnusedCacheFiles,
Done,
}
private readonly AssetsPackage _package;
private List<string> _unusedCacheFilePaths;
private int _unusedFileTotalCount = 0;
private ESteps _steps = ESteps.None;
internal ClearUnusedCacheFilesOperation(AssetsPackage package)
{
_package = package;
}
internal override void Start()
{
_steps = ESteps.GetUnusedCacheFiles;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetUnusedCacheFiles)
{
_unusedCacheFilePaths = GetUnusedCacheFilePaths();
_unusedFileTotalCount = _unusedCacheFilePaths.Count;
YooLogger.Log($"Found unused cache file count : {_unusedFileTotalCount}");
_steps = ESteps.ClearUnusedCacheFiles;
}
if (_steps == ESteps.ClearUnusedCacheFiles)
{
for (int i = _unusedCacheFilePaths.Count - 1; i >= 0; i--)
{
string filePath = _unusedCacheFilePaths[i];
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
YooLogger.Log($"Delete unused cache file : {filePath}");
}
catch (System.Exception e)
{
YooLogger.Warning($"Failed delete cache file : {filePath} ! {e.Message}");
}
}
_unusedCacheFilePaths.RemoveAt(i);
if (OperationSystem.IsBusy)
break;
}
if (_unusedFileTotalCount == 0)
Progress = 1.0f;
else
Progress = 1.0f - (_unusedCacheFilePaths.Count / _unusedFileTotalCount);
if (_unusedCacheFilePaths.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
/// <summary>
/// 获取未被使用的缓存文件路径集合
/// </summary>
private List<string> GetUnusedCacheFilePaths()
{
string cacheFolderPath = PersistentHelper.GetCacheFolderPath(_package.PackageName);
if (Directory.Exists(cacheFolderPath) == false)
return new List<string>();
DirectoryInfo directoryInfo = new DirectoryInfo(cacheFolderPath);
FileInfo[] fileInfos = directoryInfo.GetFiles();
List<string> result = new List<string>(fileInfos.Length);
foreach (FileInfo fileInfo in fileInfos)
{
if (_package.IsIncludeBundleFile(fileInfo.Name) == false)
{
result.Add(fileInfo.FullName);
}
}
return result;
}
}
}

View File

@ -79,7 +79,7 @@ namespace YooAsset
None,
QueryBuildinPackageVersion,
LoadBuildinManifest,
VerifyPackage,
PackageCaching,
Done,
}
@ -87,7 +87,7 @@ namespace YooAsset
private readonly string _packageName;
private QueryBuildinPackageVersionOperation _queryBuildinPackageVersionOp;
private LoadBuildinManifestOperation _loadBuildinManifestOp;
private VerifyPackageOperation _verifyOperation;
private PackageCachingOperation _cachingOperation;
private ESteps _steps = ESteps.None;
internal OfflinePlayModeInitializationOperation(OfflinePlayModeImpl impl, string packageName)
@ -143,7 +143,7 @@ namespace YooAsset
{
PackageVersion = _loadBuildinManifestOp.Manifest.PackageVersion;
_impl.ActiveManifest = _loadBuildinManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
_steps = ESteps.PackageCaching;
}
else
{
@ -153,16 +153,16 @@ namespace YooAsset
}
}
if (_steps == ESteps.VerifyPackage)
if (_steps == ESteps.PackageCaching)
{
if (_verifyOperation == null)
if (_cachingOperation == null)
{
_verifyOperation = VerifyPackageOperation.CreateOperation(_impl.ActiveManifest, _impl);
OperationSystem.StartOperation(_verifyOperation);
_cachingOperation = new PackageCachingOperation(_packageName);
OperationSystem.StartOperation(_cachingOperation);
}
Progress = _verifyOperation.Progress;
if (_verifyOperation.IsDone)
Progress = _cachingOperation.Progress;
if (_cachingOperation.IsDone)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
@ -186,7 +186,7 @@ namespace YooAsset
QueryBuildinPackageVersion,
UnpackBuildinManifest,
LoadBuildinManifest,
VerifyPackage,
PackageCaching,
Done,
}
@ -197,7 +197,7 @@ namespace YooAsset
private UnpackBuildinManifestOperation _unpackBuildinManifestOp;
private LoadBuildinManifestOperation _loadBuildinManifestOp;
private LoadCacheManifestOperation _loadCacheManifestOp;
private VerifyPackageOperation _verifyOperation;
private PackageCachingOperation _cachingOperation;
private ESteps _steps = ESteps.None;
internal HostPlayModeInitializationOperation(HostPlayModeImpl impl, string packageName)
@ -265,7 +265,7 @@ namespace YooAsset
{
PackageVersion = _loadCacheManifestOp.Manifest.PackageVersion;
_impl.ActiveManifest = _loadCacheManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
_steps = ESteps.PackageCaching;
}
else
{
@ -284,15 +284,14 @@ namespace YooAsset
if (_queryBuildinPackageVersionOp.IsDone == false)
return;
// 注意为了兼容MOD模式初始化动态新增的包裹的时候如果内置清单不存在也不需要报错
if (_queryBuildinPackageVersionOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.UnpackBuildinManifest;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
// 注意为了兼容MOD模式初始化动态新增的包裹的时候如果内置清单不存在也不需要报错
_steps = ESteps.PackageCaching;
string error = _queryBuildinPackageVersionOp.Error;
YooLogger.Log($"Failed to load buildin package version file : {error}");
}
@ -337,7 +336,7 @@ namespace YooAsset
{
PackageVersion = _loadBuildinManifestOp.Manifest.PackageVersion;
_impl.ActiveManifest = _loadBuildinManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
_steps = ESteps.PackageCaching;
}
else
{
@ -347,16 +346,16 @@ namespace YooAsset
}
}
if (_steps == ESteps.VerifyPackage)
if (_steps == ESteps.PackageCaching)
{
if (_verifyOperation == null)
if (_cachingOperation == null)
{
_verifyOperation = VerifyPackageOperation.CreateOperation(_impl.ActiveManifest, _impl);
OperationSystem.StartOperation(_verifyOperation);
_cachingOperation = new PackageCachingOperation(_packageName);
OperationSystem.StartOperation(_cachingOperation);
}
Progress = _verifyOperation.Progress;
if (_verifyOperation.IsDone)
Progress = _cachingOperation.Progress;
if (_cachingOperation.IsDone)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;

View File

@ -1,294 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace YooAsset
{
internal abstract class VerifyPackageOperation : AsyncOperationBase
{
public List<VerifyInfo> VerifySuccessList { protected set; get; }
public List<VerifyInfo> VerifyFailList { protected set; get; }
public static VerifyPackageOperation CreateOperation(PatchManifest manifest, IPlayModeServices playModeServices)
{
#if UNITY_WEBGL
VerifyPackageOperation operation = new VerifyPackageWithoutThreadOperation(manifest, playModeServices);
#else
VerifyPackageOperation operation = new VerifyPackageWithThreadOperation(manifest, playModeServices);
#endif
return operation;
}
}
/// <summary>
/// 本地缓存文件验证(线程版)
/// </summary>
internal class VerifyPackageWithThreadOperation : VerifyPackageOperation
{
private enum ESteps
{
None,
InitVerify,
PrepareVerify,
UpdateVerify,
Done,
}
private readonly PatchManifest _manifest;
private readonly IPlayModeServices _playModeServices;
private readonly ThreadSyncContext _syncContext = new ThreadSyncContext();
private List<VerifyInfo> _waitingList;
private List<VerifyInfo> _verifyingList;
private int _verifyMaxNum;
private int _verifyTotalCount;
private float _verifyStartTime;
private ESteps _steps = ESteps.None;
public VerifyPackageWithThreadOperation(PatchManifest manifest, IPlayModeServices playModeServices)
{
_manifest = manifest;
_playModeServices = playModeServices;
}
internal override void Start()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.InitVerify)
{
int bundleCount = _manifest.BundleList.Count;
VerifySuccessList = new List<VerifyInfo>(bundleCount);
VerifyFailList = new List<VerifyInfo>(bundleCount);
// 设置同时验证的最大数
ThreadPool.GetMaxThreads(out int workerThreads, out int ioThreads);
YooLogger.Log($"Work threads : {workerThreads}, IO threads : {ioThreads}");
_verifyMaxNum = Math.Min(workerThreads, ioThreads);
_verifyTotalCount = bundleCount;
if (_verifyMaxNum < 1)
_verifyMaxNum = 1;
_waitingList = new List<VerifyInfo>(bundleCount);
_verifyingList = new List<VerifyInfo>(_verifyMaxNum);
_steps = ESteps.PrepareVerify;
}
if (_steps == ESteps.PrepareVerify)
{
foreach (var patchBundle in _manifest.BundleList)
{
if (CacheSystem.IsCached(patchBundle))
continue;
bool isBuildinFile = _playModeServices.IsBuildinPatchBundle(patchBundle);
VerifyInfo verifyInfo = new VerifyInfo(isBuildinFile, patchBundle);
_waitingList.Add(verifyInfo);
}
_steps = ESteps.UpdateVerify;
}
if (_steps == ESteps.UpdateVerify)
{
_syncContext.Update();
Progress = GetVerifierProgress();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Verify elapsed time {costTime:f1} seconds");
}
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (OperationSystem.IsBusy)
break;
if (_verifyingList.Count >= _verifyMaxNum)
break;
var verifyIno = _waitingList[i];
if (VerifyFileWithThread(verifyIno))
{
_waitingList.RemoveAt(i);
_verifyingList.Add(verifyIno);
}
else
{
YooLogger.Warning("The thread pool is failed queued.");
break;
}
}
}
}
private float GetVerifierProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(VerifySuccessList.Count + VerifyFailList.Count) / _verifyTotalCount;
}
private bool VerifyFileWithThread(VerifyInfo verifyInfo)
{
return ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyInThread), verifyInfo);
}
private void VerifyInThread(object infoObj)
{
VerifyInfo verifyInfo = (VerifyInfo)infoObj;
verifyInfo.Result = CacheSystem.VerifyBundle(verifyInfo.VerifyBundle, CacheSystem.InitVerifyLevel);
_syncContext.Post(VerifyCallback, verifyInfo);
}
private void VerifyCallback(object obj)
{
VerifyInfo verifyIno = (VerifyInfo)obj;
if (verifyIno.Result == EVerifyResult.Succeed)
{
VerifySuccessList.Add(verifyIno);
CacheSystem.CacheBundle(verifyIno.VerifyBundle);
}
else
{
VerifyFailList.Add(verifyIno);
// 删除验证失败的缓存文件
if (File.Exists(verifyIno.VerifyBundle.CachedFilePath))
{
YooLogger.Warning($"Delete verify failed bundle file : {verifyIno.VerifyBundle.CachedFilePath}");
File.Delete(verifyIno.VerifyBundle.CachedFilePath);
}
}
_verifyingList.Remove(verifyIno);
}
}
/// <summary>
/// 本地缓存文件验证(非线程版)
/// </summary>
internal class VerifyPackageWithoutThreadOperation : VerifyPackageOperation
{
private enum ESteps
{
None,
InitVerify,
PrepareVerify,
UpdateVerify,
Done,
}
private readonly PatchManifest _manifest;
private readonly IPlayModeServices _playModeServices;
private List<VerifyInfo> _waitingList;
private List<VerifyInfo> _verifyingList;
private int _verifyMaxNum;
private int _verifyTotalCount;
private float _verifyStartTime;
private ESteps _steps = ESteps.None;
public VerifyPackageWithoutThreadOperation(PatchManifest manifest, IPlayModeServices playModeServices)
{
_manifest = manifest;
_playModeServices = playModeServices;
}
internal override void Start()
{
_steps = ESteps.InitVerify;
_verifyStartTime = UnityEngine.Time.realtimeSinceStartup;
}
internal override void Update()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.InitVerify)
{
int bundleCount = _manifest.BundleList.Count;
VerifySuccessList = new List<VerifyInfo>(bundleCount);
VerifyFailList = new List<VerifyInfo>(bundleCount);
// 设置同时验证的最大数
_verifyMaxNum = bundleCount;
_verifyTotalCount = bundleCount;
_waitingList = new List<VerifyInfo>(bundleCount);
_verifyingList = new List<VerifyInfo>(_verifyMaxNum);
_steps = ESteps.PrepareVerify;
}
if (_steps == ESteps.PrepareVerify)
{
foreach (var patchBundle in _manifest.BundleList)
{
if (CacheSystem.IsCached(patchBundle))
continue;
bool isBuildinFile = _playModeServices.IsBuildinPatchBundle(patchBundle);
VerifyInfo verifyInfo = new VerifyInfo(isBuildinFile, patchBundle);
_waitingList.Add(verifyInfo);
}
_steps = ESteps.UpdateVerify;
}
if (_steps == ESteps.UpdateVerify)
{
Progress = GetVerifierProgress();
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
float costTime = UnityEngine.Time.realtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Verify elapsed time {costTime:f1} seconds");
}
for (int i = _waitingList.Count - 1; i >= 0; i--)
{
if (OperationSystem.IsBusy)
break;
if (_verifyingList.Count >= _verifyMaxNum)
break;
var verifyIno = _waitingList[i];
VerifyFileWithoutThread(verifyIno);
_waitingList.RemoveAt(i);
_verifyingList.Add(verifyIno);
}
_verifyingList.Clear();
}
}
private float GetVerifierProgress()
{
if (_verifyTotalCount == 0)
return 1f;
return (float)(VerifySuccessList.Count + VerifyFailList.Count) / _verifyTotalCount;
}
private void VerifyFileWithoutThread(VerifyInfo verifyIno)
{
var verifyResult = CacheSystem.VerifyAndCacheLocalBundleFile(verifyIno.VerifyBundle, CacheSystem.InitVerifyLevel);
if (verifyResult == EVerifyResult.Succeed)
{
VerifySuccessList.Add(verifyIno);
}
else
{
VerifyFailList.Add(verifyIno);
// 删除验证失败的缓存文件
if (File.Exists(verifyIno.VerifyBundle.CachedFilePath))
{
YooLogger.Warning($"Delete verify failed bundle file : {verifyIno.VerifyBundle.CachedFilePath}");
File.Delete(verifyIno.VerifyBundle.CachedFilePath);
}
}
}
}
}

View File

@ -47,7 +47,7 @@ namespace YooAsset
/// <summary>
/// 联机模式的更新清单操作
/// 注意:优先加载沙盒里缓存的清单文件,如果有变化就更新远端清单文件,并保存到本地。
/// 注意:优先加载沙盒里缓存的清单文件,如果缓存没找到就下载远端清单文件,并保存到本地。
/// </summary>
internal sealed class HostPlayModeUpdatePackageManifestOperation : UpdatePackageManifestOperation
{
@ -59,7 +59,6 @@ namespace YooAsset
DownloadManifest,
LoadCacheManifest,
CheckDeserializeManifest,
VerifyPackage,
Done,
}
@ -70,7 +69,6 @@ namespace YooAsset
private LoadCacheManifestOperation _tryLoadCacheManifestOp;
private LoadCacheManifestOperation _loadCacheManifestOp;
private DownloadManifestOperation _downloadManifestOp;
private VerifyPackageOperation _verifyOperation;
private ESteps _steps = ESteps.None;
@ -118,7 +116,8 @@ namespace YooAsset
if (_tryLoadCacheManifestOp.Status == EOperationStatus.Succeed)
{
_impl.ActiveManifest = _tryLoadCacheManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
@ -163,7 +162,8 @@ namespace YooAsset
if (_loadCacheManifestOp.Status == EOperationStatus.Succeed)
{
_impl.ActiveManifest = _loadCacheManifestOp.Manifest;
_steps = ESteps.VerifyPackage;
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
@ -172,22 +172,6 @@ namespace YooAsset
Error = _loadCacheManifestOp.Error;
}
}
if (_steps == ESteps.VerifyPackage)
{
if (_verifyOperation == null)
{
_verifyOperation = VerifyPackageOperation.CreateOperation(_impl.ActiveManifest, _impl);
OperationSystem.StartOperation(_verifyOperation);
}
Progress = _verifyOperation.Progress;
if (_verifyOperation.IsDone)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
}
}
}
}

View File

@ -45,22 +45,63 @@ namespace YooAsset
/// <summary>
/// 所属的包裹名称
/// </summary>
private string _packageName;
public string PackageName { private set; get; }
/// <summary>
/// 缓存文件路径
/// 缓存GUID
/// </summary>
private string _cachedFilePath;
public string CachedFilePath
public string CacheGUID
{
get { return FileHash; }
}
/// <summary>
/// 缓存的数据文件路径
/// </summary>
private string _cachedDataFilePath;
public string CachedDataFilePath
{
get
{
if (string.IsNullOrEmpty(_cachedFilePath) == false)
return _cachedFilePath;
if (string.IsNullOrEmpty(_cachedDataFilePath) == false)
return _cachedDataFilePath;
string cacheRoot = PersistentHelper.GetCacheFolderPath(_packageName);
_cachedFilePath = $"{cacheRoot}/{FileName}";
return _cachedFilePath;
string cacheRoot = PersistentHelper.GetCacheFolderPath(PackageName);
_cachedDataFilePath = $"{cacheRoot}/{CacheGUID}/{YooAssetSettings.CacheBundleDataFileName}";
return _cachedDataFilePath;
}
}
/// <summary>
/// 缓存的信息文件路径
/// </summary>
private string _cachedInfoFilePath;
public string CachedInfoFilePath
{
get
{
if (string.IsNullOrEmpty(_cachedInfoFilePath) == false)
return _cachedInfoFilePath;
string cacheRoot = PersistentHelper.GetCacheFolderPath(PackageName);
_cachedInfoFilePath = $"{cacheRoot}/{CacheGUID}/{YooAssetSettings.CacheBundleInfoFileName}";
return _cachedInfoFilePath;
}
}
/// <summary>
/// 临时的数据文件路径
/// </summary>
private string _tempDataFilePath;
public string TempDataFilePath
{
get
{
if (string.IsNullOrEmpty(_tempDataFilePath) == false)
return _tempDataFilePath;
_tempDataFilePath = $"{CachedDataFilePath}.temp";
return _tempDataFilePath;
}
}
@ -81,7 +122,7 @@ namespace YooAsset
}
/// <summary>
/// 文件名称
/// 文件名称(远端文件名和内置文件名)
/// </summary>
private string _fileName;
public string FileName
@ -94,20 +135,6 @@ namespace YooAsset
}
}
/// <summary>
/// 缓存查询Key
/// </summary>
private string _cacheKey;
public string CacheKey
{
get
{
if (string.IsNullOrEmpty(_cacheKey))
throw new Exception("Should never get here !");
return _cacheKey;
}
}
public PatchBundle()
{
@ -118,8 +145,7 @@ namespace YooAsset
/// </summary>
public void ParseBundle(string packageName, int nameStype)
{
_packageName = packageName;
_cacheKey = $"{packageName}-{FileHash}";
PackageName = packageName;
_fileName = PatchManifestTools.CreateBundleFileName(nameStype, BundleName, FileHash, IsRawFile);
}

View File

@ -239,11 +239,11 @@ namespace YooAsset
/// <summary>
/// 是否包含资源文件
/// </summary>
public bool IsIncludeBundleFile(string fileName)
public bool IsIncludeBundleFile(string cacheGUID)
{
foreach (var patchBundle in BundleList)
{
if (patchBundle.FileName == fileName)
if (patchBundle.CacheGUID == cacheGUID)
return true;
}
return false;

View File

@ -159,13 +159,13 @@ namespace YooAsset
{
if (nameStyle == 1) //HashName
{
string fileExtension = isRawFile ? YooAssetSettingsData.Setting.RawFileVariant : YooAssetSettingsData.Setting.AssetBundleFileVariant;
string fileExtension = isRawFile ? YooAssetSettingsData.Setting.RawBundleFileVariant : YooAssetSettingsData.Setting.AssetBundleFileVariant;
return StringUtility.Format("{0}.{1}", fileHash, fileExtension);
}
else if (nameStyle == 4) //BundleName_HashName
{
string fileName = bundleName.Remove(bundleName.LastIndexOf('.'));
string fileExtension = isRawFile ? YooAssetSettingsData.Setting.RawFileVariant : YooAssetSettingsData.Setting.AssetBundleFileVariant;
string fileExtension = isRawFile ? YooAssetSettingsData.Setting.RawBundleFileVariant : YooAssetSettingsData.Setting.AssetBundleFileVariant;
return StringUtility.Format("{0}_{1}.{2}", fileName, fileHash, fileExtension);
}
else

View File

@ -56,12 +56,6 @@ namespace YooAsset
OperationSystem.StartOperation(operation);
return operation;
}
CheckPackageContentsOperation IPlayModeServices.CheckPackageContentsOperation(string packageVersion)
{
var operation = new EditorSimulateModeCheckPackageContentsOperation();
OperationSystem.StartOperation(operation);
return operation;
}
PatchDownloaderOperation IPlayModeServices.CreatePatchDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout)
{

View File

@ -95,6 +95,10 @@ namespace YooAsset
{
return _queryServices.QueryStreamingAssets(patchBundle.FileName);
}
public bool IsCachedPatchBundle(PatchBundle patchBundle)
{
return CacheSystem.IsCached(patchBundle.PackageName, patchBundle.CacheGUID);
}
UpdatePackageVersionOperation IPlayModeServices.UpdatePackageVersionAsync(bool appendTimeTicks, int timeout)
{
@ -114,12 +118,6 @@ namespace YooAsset
OperationSystem.StartOperation(operation);
return operation;
}
CheckPackageContentsOperation IPlayModeServices.CheckPackageContentsOperation(string packageVersion)
{
var operation = new HostPlayModeCheckPackageContentsOperation(this, _packageName, packageVersion);
OperationSystem.StartOperation(operation);
return operation;
}
PatchDownloaderOperation IPlayModeServices.CreatePatchDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout)
{
@ -133,7 +131,7 @@ namespace YooAsset
foreach (var patchBundle in patchManifest.BundleList)
{
// 忽略缓存文件
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
continue;
// 忽略APP资源
@ -158,7 +156,7 @@ namespace YooAsset
foreach (var patchBundle in patchManifest.BundleList)
{
// 忽略缓存文件
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
continue;
// 忽略APP资源
@ -219,7 +217,7 @@ namespace YooAsset
foreach (var patchBundle in checkList)
{
// 忽略缓存文件
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
continue;
// 忽略APP资源
@ -244,7 +242,7 @@ namespace YooAsset
foreach (var patchBundle in patchManifest.BundleList)
{
// 忽略缓存文件
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
continue;
if (IsBuildinPatchBundle(patchBundle))
@ -268,7 +266,7 @@ namespace YooAsset
foreach (var patchBundle in patchManifest.BundleList)
{
// 忽略缓存文件
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
continue;
// 查询DLC资源
@ -292,7 +290,7 @@ namespace YooAsset
throw new Exception("Should never get here !");
// 查询沙盒资源
if (CacheSystem.IsCached(patchBundle))
if (IsCachedPatchBundle(patchBundle))
{
BundleInfo bundleInfo = new BundleInfo(patchBundle, BundleInfo.ELoadMode.LoadFromCache);
return bundleInfo;

View File

@ -56,12 +56,6 @@ namespace YooAsset
OperationSystem.StartOperation(operation);
return operation;
}
CheckPackageContentsOperation IPlayModeServices.CheckPackageContentsOperation(string packageVersion)
{
var operation = new OfflinePlayModeCheckPackageContentsOperation();
OperationSystem.StartOperation(operation);
return operation;
}
PatchDownloaderOperation IPlayModeServices.CreatePatchDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout)
{
@ -93,7 +87,7 @@ namespace YooAsset
throw new Exception("Should never get here !");
// 查询沙盒资源
if (CacheSystem.IsCached(patchBundle))
if (CacheSystem.IsCached(patchBundle.PackageName, patchBundle.CacheGUID))
{
BundleInfo bundleInfo = new BundleInfo(patchBundle, BundleInfo.ELoadMode.LoadFromCache);
return bundleInfo;

View File

@ -28,11 +28,6 @@ namespace YooAsset
/// </summary>
PreDownloadPackageOperation PreDownloadPackageAsync(string packageVersion, int timeout);
/// <summary>
/// 检查包裹内容的完整性
/// </summary>
CheckPackageContentsOperation CheckPackageContentsOperation(string packageVersion);
// 下载相关
PatchDownloaderOperation CreatePatchDownloaderByAll(int downloadingMaxNumber, int failedTryAgain, int timeout);
PatchDownloaderOperation CreatePatchDownloaderByTags(string[] tags, int downloadingMaxNumber, int failedTryAgain, int timeout);

View File

@ -8,12 +8,12 @@ namespace YooAsset
/// <summary>
/// AssetBundle文件的后缀名
/// </summary>
public string BundleFileVariant = "bundle";
public string AssetBundleFileVariant = "bundle";
/// <summary>
/// 原生文件的后缀名
/// </summary>
public string RawFileVariant = "rawfile";
public string RawBundleFileVariant = "rawfile";
/// <summary>
/// 清单文件名称

View File

@ -79,9 +79,9 @@ namespace YooAsset
{
string shareBundleName;
if (uniqueBundleName)
shareBundleName = $"{packageName.ToLower()}_{YooAssetSettings.UnityShadersBundleName}.{Setting.BundleFileVariant}";
shareBundleName = $"{packageName.ToLower()}_{YooAssetSettings.UnityShadersBundleName}.{Setting.AssetBundleFileVariant}";
else
shareBundleName = $"{YooAssetSettings.UnityShadersBundleName}.{Setting.BundleFileVariant}";
shareBundleName = $"{YooAssetSettings.UnityShadersBundleName}.{Setting.AssetBundleFileVariant}";
return shareBundleName.ToLower();
}
}