2024-11-29 21:37:01 +08:00

621 lines
23 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 缓存文件系统
/// 说明正在进行的下载器会在ResourcePackage销毁的时候执行Abort操作
/// </summary>
internal class DefaultCacheFileSystem : IFileSystem
{
public class FileWrapper
{
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 FileWrapper(string infoFilePath, string dataFilePath, string dataFileCRC, long dataFileSize)
{
InfoFilePath = infoFilePath;
DataFilePath = dataFilePath;
DataFileCRC = dataFileCRC;
DataFileSize = dataFileSize;
}
}
protected readonly Dictionary<string, DefaultDownloadFileOperation> _downloaders = new Dictionary<string, DefaultDownloadFileOperation>(1000);
protected readonly Dictionary<string, FileWrapper> _wrappers = new Dictionary<string, FileWrapper>(10000);
protected readonly Dictionary<string, Stream> _loadedStream = new Dictionary<string, Stream>(10000);
protected readonly Dictionary<string, string> _dataFilePaths = new Dictionary<string, string>(10000);
protected readonly Dictionary<string, string> _infoFilePaths = new Dictionary<string, string>(10000);
protected readonly Dictionary<string, string> _tempFilePaths = new Dictionary<string, string>(10000);
protected readonly List<string> _removeList = new List<string>(1000);
protected string _packageRoot;
protected string _saveFileRoot;
protected string _tempFileRoot;
protected string _manifestFileRoot;
/// <summary>
/// 包裹名称
/// </summary>
public string PackageName { private set; get; }
/// <summary>
/// 文件根目录
/// </summary>
public string FileRoot
{
get
{
return _packageRoot;
}
}
/// <summary>
/// 文件数量
/// </summary>
public int FileCount
{
get
{
return _wrappers.Count;
}
}
#region
/// <summary>
/// 自定义参数:远程服务接口
/// </summary>
public IRemoteServices RemoteServices { private set; get; } = null;
/// <summary>
/// 自定义参数:初始化的时候缓存文件校验级别
/// </summary>
public EFileVerifyLevel FileVerifyLevel { private set; get; } = EFileVerifyLevel.Middle;
/// <summary>
/// 自定义参数:数据文件追加文件格式
/// </summary>
public bool AppendFileExtension { private set; get; } = false;
/// <summary>
/// 自定义参数:原生文件构建管线
/// </summary>
public bool RawFileBuildPipeline { private set; get; } = false;
/// <summary>
/// 自定义参数:启用断点续传的最小尺寸
/// </summary>
public long ResumeDownloadMinimumSize { private set; get; } = long.MaxValue;
/// <summary>
/// 自定义参数:断点续传下载器关注的错误码
/// </summary>
public List<long> ResumeDownloadResponseCodes { private set; get; } = null;
/// <summary>
/// 自定义参数:解密方法类
/// </summary>
public IDecryptionServices DecryptionServices { private set; get; }
#endregion
public DefaultCacheFileSystem()
{
}
public virtual FSInitializeFileSystemOperation InitializeFileSystemAsync()
{
var operation = new DCFSInitializeOperation(this);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSLoadPackageManifestOperation LoadPackageManifestAsync(string packageVersion, int timeout)
{
var operation = new DCFSLoadPackageManifestOperation(this, packageVersion, timeout);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSRequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks, int timeout)
{
var operation = new DCFSRequestPackageVersionOperation(this, appendTimeTicks, timeout);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSClearAllBundleFilesOperation ClearAllBundleFilesAsync()
{
var operation = new DCFSClearAllBundleFilesOperation(this);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSClearUnusedBundleFilesOperation ClearUnusedBundleFilesAsync(PackageManifest manifest)
{
var operation = new DCFSClearUnusedBundleFilesOperation(this, manifest);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadParam param)
{
// 查询旧的下载器
if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader))
{
oldDownloader.Reference();
return oldDownloader;
}
// 创建新的下载器
{
if (string.IsNullOrEmpty(param.ImportFilePath))
{
param.MainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
param.FallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
}
else
{
// 注意:把本地文件路径指定为远端下载地址
param.MainURL = DownloadSystemHelper.ConvertToWWWPath(param.ImportFilePath);
param.FallbackURL = param.MainURL;
}
if (bundle.FileSize >= ResumeDownloadMinimumSize)
{
var newDownloader = new DCFSDownloadResumeFileOperation(this, bundle, param);
newDownloader.Reference();
_downloaders.Add(bundle.BundleGUID, newDownloader);
OperationSystem.StartOperation(PackageName, newDownloader);
return newDownloader;
}
else
{
var newDownloader = new DCFSDownloadNormalFileOperation(this, bundle, param);
newDownloader.Reference();
_downloaders.Add(bundle.BundleGUID, newDownloader);
OperationSystem.StartOperation(PackageName, newDownloader);
return newDownloader;
}
}
}
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{
if (RawFileBuildPipeline)
{
var operation = new DCFSLoadRawBundleOperation(this, bundle);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
else
{
var operation = new DCFSLoadAssetBundleOperation(this, bundle);
OperationSystem.StartOperation(PackageName, operation);
return operation;
}
}
public virtual void UnloadBundleFile(PackageBundle bundle, object result)
{
AssetBundle assetBundle = result as AssetBundle;
if (assetBundle == null)
return;
if (assetBundle != null)
assetBundle.Unload(true);
if (_loadedStream.TryGetValue(bundle.BundleGUID, out Stream managedStream))
{
if (managedStream != null)
{
managedStream.Close();
managedStream.Dispose();
}
_loadedStream.Remove(bundle.BundleGUID);
}
}
public virtual void SetParameter(string name, object value)
{
if (name == FileSystemParametersDefine.REMOTE_SERVICES)
{
RemoteServices = (IRemoteServices)value;
}
else if (name == FileSystemParametersDefine.FILE_VERIFY_LEVEL)
{
FileVerifyLevel = (EFileVerifyLevel)value;
}
else if (name == FileSystemParametersDefine.APPEND_FILE_EXTENSION)
{
AppendFileExtension = (bool)value;
}
else if (name == FileSystemParametersDefine.RAW_FILE_BUILD_PIPELINE)
{
RawFileBuildPipeline = (bool)value;
}
else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_MINMUM_SIZE)
{
ResumeDownloadMinimumSize = (long)value;
}
else if (name == FileSystemParametersDefine.RESUME_DOWNLOAD_RESPONSE_CODES)
{
ResumeDownloadResponseCodes = (List<long>)value;
}
else if (name == FileSystemParametersDefine.DECRYPTION_SERVICES)
{
DecryptionServices = (IDecryptionServices)value;
}
else
{
YooLogger.Warning($"Invalid parameter : {name}");
}
}
public virtual void OnCreate(string packageName, string rootDirectory)
{
PackageName = packageName;
if (string.IsNullOrEmpty(rootDirectory))
rootDirectory = GetDefaultRoot();
_packageRoot = PathUtility.Combine(rootDirectory, packageName);
_saveFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.SaveFilesFolderName);
_tempFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.TempFilesFolderName);
_manifestFileRoot = PathUtility.Combine(_packageRoot, DefaultCacheFileSystemDefine.ManifestFilesFolderName);
}
public virtual void OnUpdate()
{
_removeList.Clear();
foreach (var valuePair in _downloaders)
{
var downloader = valuePair.Value;
// 注意:主动终止引用计数为零的下载任务
if (downloader.RefCount <= 0)
{
_removeList.Add(valuePair.Key);
downloader.SetAbort();
continue;
}
if (downloader.IsDone)
_removeList.Add(valuePair.Key);
}
foreach (var key in _removeList)
{
_downloaders.Remove(key);
}
}
public virtual bool Belong(PackageBundle bundle)
{
// 注意:缓存文件系统保底加载!
return true;
}
public virtual bool Exists(PackageBundle bundle)
{
return _wrappers.ContainsKey(bundle.BundleGUID);
}
public virtual bool NeedDownload(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
return Exists(bundle) == false;
}
public virtual bool NeedUnpack(PackageBundle bundle)
{
return false;
}
public virtual bool NeedImport(PackageBundle bundle)
{
if (Belong(bundle) == false)
return false;
return Exists(bundle) == false;
}
public virtual byte[] ReadFileData(PackageBundle bundle)
{
if (Exists(bundle) == false)
return null;
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetCacheFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileData(fileInfo);
}
else
{
string filePath = GetCacheFileLoadPath(bundle);
return FileUtility.ReadAllBytes(filePath);
}
}
public virtual string ReadFileText(PackageBundle bundle)
{
if (Exists(bundle) == false)
return null;
if (bundle.Encrypted)
{
if (DecryptionServices == null)
{
YooLogger.Error($"The {nameof(IDecryptionServices)} is null !");
return null;
}
string filePath = GetCacheFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
return DecryptionServices.ReadFileText(fileInfo);
}
else
{
string filePath = GetCacheFileLoadPath(bundle);
return FileUtility.ReadAllText(filePath);
}
}
#region
private readonly BufferWriter _sharedBuffer = new BufferWriter(1024);
public void WriteInfoFile(string filePath, string dataFileCRC, long dataFileSize)
{
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read))
{
_sharedBuffer.Clear();
_sharedBuffer.WriteUTF8(dataFileCRC);
_sharedBuffer.WriteInt64(dataFileSize);
_sharedBuffer.WriteToStream(fs);
fs.Flush();
}
}
public void ReadInfoFile(string filePath, out string dataFileCRC, out long dataFileSize)
{
byte[] binaryData = FileUtility.ReadAllBytes(filePath);
BufferReader buffer = new BufferReader(binaryData);
dataFileCRC = buffer.ReadUTF8();
dataFileSize = buffer.ReadInt64();
}
protected string GetDefaultRoot()
{
#if UNITY_EDITOR
// 注意:为了方便调试查看,编辑器下把存储目录放到项目里。
string projectPath = Path.GetDirectoryName(UnityEngine.Application.dataPath);
projectPath = PathUtility.RegularPath(projectPath);
return PathUtility.Combine(projectPath, YooAssetSettingsData.Setting.DefaultYooFolderName);
#elif UNITY_STANDALONE
return PathUtility.Combine(UnityEngine.Application.dataPath, YooAssetSettingsData.Setting.DefaultYooFolderName);
#else
return PathUtility.Combine(UnityEngine.Application.persistentDataPath, YooAssetSettingsData.Setting.DefaultYooFolderName);
#endif
}
protected string GetDataFilePath(PackageBundle bundle)
{
if (_dataFilePaths.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
string folderName = bundle.FileHash.Substring(0, 2);
filePath = PathUtility.Combine(_saveFileRoot, folderName, bundle.BundleGUID, DefaultCacheFileSystemDefine.SaveBundleDataFileName);
if (AppendFileExtension)
filePath += bundle.FileExtension;
_dataFilePaths.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
protected string GetInfoFilePath(PackageBundle bundle)
{
if (_infoFilePaths.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
string folderName = bundle.FileHash.Substring(0, 2);
filePath = PathUtility.Combine(_saveFileRoot, folderName, bundle.BundleGUID, DefaultCacheFileSystemDefine.SaveBundleInfoFileName);
_infoFilePaths.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
public string GetTempFilePath(PackageBundle bundle)
{
if (_tempFilePaths.TryGetValue(bundle.BundleGUID, out string filePath) == false)
{
filePath = PathUtility.Combine(_tempFileRoot, bundle.BundleGUID);
_tempFilePaths.Add(bundle.BundleGUID, filePath);
}
return filePath;
}
public string GetCacheFileLoadPath(PackageBundle bundle)
{
return GetDataFilePath(bundle);
}
public string GetCacheFilesRoot()
{
return _saveFileRoot;
}
public string GetCachePackageHashFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetPackageHashFileName(PackageName, packageVersion);
return PathUtility.Combine(_manifestFileRoot, fileName);
}
public string GetCachePackageManifestFilePath(string packageVersion)
{
string fileName = YooAssetSettingsData.GetManifestBinaryFileName(PackageName, packageVersion);
return PathUtility.Combine(_manifestFileRoot, fileName);
}
public string GetSandboxAppFootPrintFilePath()
{
return PathUtility.Combine(_manifestFileRoot, DefaultCacheFileSystemDefine.AppFootPrintFileName);
}
/// <summary>
/// 是否已经记录了文件
/// </summary>
public bool IsRecordFile(string bundleGUID)
{
return _wrappers.ContainsKey(bundleGUID);
}
/// <summary>
/// 记录文件信息
/// </summary>
public bool RecordFile(string bundleGUID, FileWrapper wrapper)
{
if (_wrappers.ContainsKey(bundleGUID))
{
YooLogger.Error($"{nameof(DefaultCacheFileSystem)} has element : {bundleGUID}");
return false;
}
_wrappers.Add(bundleGUID, wrapper);
return true;
}
/// <summary>
/// 验证缓存文件
/// </summary>
public EFileVerifyResult VerifyCacheFile(PackageBundle bundle)
{
if (_wrappers.TryGetValue(bundle.BundleGUID, out FileWrapper wrapper) == false)
return EFileVerifyResult.CacheNotFound;
EFileVerifyResult result = FileSystemHelper.FileVerify(wrapper.DataFilePath, wrapper.DataFileSize, wrapper.DataFileCRC, EFileVerifyLevel.High);
return result;
}
/// <summary>
/// 写入缓存文件
/// </summary>
public bool WriteCacheFile(PackageBundle bundle, string copyPath)
{
if (_wrappers.ContainsKey(bundle.BundleGUID))
{
throw new Exception("Should never get here !");
}
string infoFilePath = GetInfoFilePath(bundle);
string dataFilePath = GetDataFilePath(bundle);
try
{
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
FileUtility.CreateFileDirectory(dataFilePath);
// 拷贝数据文件
FileInfo fileInfo = new FileInfo(copyPath);
fileInfo.CopyTo(dataFilePath);
// 写入文件信息
WriteInfoFile(infoFilePath, bundle.FileCRC, bundle.FileSize);
}
catch (Exception e)
{
YooLogger.Error($"Failed to write cache file ! {e.Message}");
return false;
}
FileWrapper wrapper = new FileWrapper(infoFilePath, dataFilePath, bundle.FileCRC, bundle.FileSize);
return RecordFile(bundle.BundleGUID, wrapper);
}
/// <summary>
/// 删除缓存文件
/// </summary>
public bool DeleteCacheFile(string bundleGUID)
{
if (_wrappers.TryGetValue(bundleGUID, out FileWrapper wrapper))
{
try
{
string dataFilePath = wrapper.DataFilePath;
FileInfo fileInfo = new FileInfo(dataFilePath);
if (fileInfo.Exists)
fileInfo.Directory.Delete(true);
_wrappers.Remove(bundleGUID);
return true;
}
catch (Exception e)
{
YooLogger.Error($"Failed to delete cache file ! {e.Message}");
return false;
}
}
else
{
return false;
}
}
/// <summary>
/// 删除所有清单文件
/// </summary>
public void DeleteAllManifestFiles()
{
if (Directory.Exists(_manifestFileRoot))
{
Directory.Delete(_manifestFileRoot, true);
}
}
/// <summary>
/// 获取所有缓存文件GUID
/// </summary>
public List<string> GetAllCachedBundleGUIDs()
{
return _wrappers.Keys.ToList();
}
/// <summary>
/// 加载加密资源文件
/// </summary>
public AssetBundle LoadEncryptedAssetBundle(PackageBundle bundle)
{
string filePath = GetCacheFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
var assetBundle = DecryptionServices.LoadAssetBundle(fileInfo, out var managedStream);
_loadedStream.Add(bundle.BundleGUID, managedStream);
return assetBundle;
}
/// <summary>
/// 加载加密资源文件
/// </summary>
public AssetBundleCreateRequest LoadEncryptedAssetBundleAsync(PackageBundle bundle)
{
string filePath = GetCacheFileLoadPath(bundle);
var fileInfo = new DecryptFileInfo()
{
BundleName = bundle.BundleName,
FileLoadCRC = bundle.UnityCRC,
FileLoadPath = filePath,
};
var createRequest = DecryptionServices.LoadAssetBundleAsync(fileInfo, out var managedStream);
_loadedStream.Add(bundle.BundleGUID, managedStream);
return createRequest;
}
#endregion
}
}