mirror of
https://github.com/tuyoogame/YooAsset.git
synced 2026-05-28 11:38:47 +00:00
refactor : 重构代码
This commit is contained in:
@@ -194,9 +194,9 @@ namespace YooAsset.Editor
|
|||||||
int playerId = args.playerId;
|
int playerId = args.playerId;
|
||||||
var debugReport = DiagnosticReport.Deserialize(args.data);
|
var debugReport = DiagnosticReport.Deserialize(args.data);
|
||||||
|
|
||||||
if (debugReport.DebuggerVersion != DiagnosticSystemDefine.DebuggerVersion)
|
if (debugReport.ProtocolVersion != DiagnosticSystemDefine.ProtocolVersion)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Debugger versions are inconsistent : {debugReport.DebuggerVersion} != {DiagnosticSystemDefine.DebuggerVersion}");
|
Debug.LogWarning($"Debugger versions are inconsistent : {debugReport.ProtocolVersion} != {DiagnosticSystemDefine.ProtocolVersion}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,10 +254,10 @@ namespace YooAsset.Editor
|
|||||||
private void OnRecordToggleValueChange(ChangeEvent<bool> evt)
|
private void OnRecordToggleValueChange(ChangeEvent<bool> evt)
|
||||||
{
|
{
|
||||||
// 发送采集数据的命令
|
// 发送采集数据的命令
|
||||||
RemoteDebugCommand command = new RemoteDebugCommand();
|
DiagnosticCommand command = new DiagnosticCommand();
|
||||||
command.CommandType = (int)EDebugCommandType.AutoSampling;
|
command.CommandType = (int)EDiagnosticCommandType.AutoSampling;
|
||||||
command.Parameter = evt.newValue ? "open" : "close";
|
command.Parameter = evt.newValue ? "open" : "close";
|
||||||
byte[] data = RemoteDebugCommand.Serialize(command);
|
byte[] data = DiagnosticCommand.Serialize(command);
|
||||||
EditorConnection.instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
EditorConnection.instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
||||||
MockEditorConnection.Instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
MockEditorConnection.Instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
||||||
}
|
}
|
||||||
@@ -265,10 +265,10 @@ namespace YooAsset.Editor
|
|||||||
private void SampleBtn_onClick()
|
private void SampleBtn_onClick()
|
||||||
{
|
{
|
||||||
// 发送采集数据的命令
|
// 发送采集数据的命令
|
||||||
RemoteDebugCommand command = new RemoteDebugCommand();
|
DiagnosticCommand command = new DiagnosticCommand();
|
||||||
command.CommandType = (int)EDebugCommandType.SampleOnce;
|
command.CommandType = (int)EDiagnosticCommandType.SampleOnce;
|
||||||
command.Parameter = string.Empty;
|
command.Parameter = string.Empty;
|
||||||
byte[] data = RemoteDebugCommand.Serialize(command);
|
byte[] data = DiagnosticCommand.Serialize(command);
|
||||||
EditorConnection.instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
EditorConnection.instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
||||||
MockEditorConnection.Instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
MockEditorConnection.Instance.Send(DiagnosticSystemDefine.EditorToPlayerMessageId, data);
|
||||||
}
|
}
|
||||||
@@ -289,7 +289,7 @@ namespace YooAsset.Editor
|
|||||||
packageData.ProviderInfos.Sort();
|
packageData.ProviderInfos.Sort();
|
||||||
foreach (var providerInfo in packageData.ProviderInfos)
|
foreach (var providerInfo in packageData.ProviderInfos)
|
||||||
{
|
{
|
||||||
providerInfo.DependentBundles.Sort();
|
providerInfo.Dependencies.Sort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -315,9 +315,9 @@ namespace YooAsset.Editor
|
|||||||
rowData.ProviderInfo = providerInfo;
|
rowData.ProviderInfo = providerInfo;
|
||||||
rowData.AddAssetPathCell("PackageName", packageData.PackageName);
|
rowData.AddAssetPathCell("PackageName", packageData.PackageName);
|
||||||
rowData.AddStringValueCell("AssetPath", providerInfo.AssetPath);
|
rowData.AddStringValueCell("AssetPath", providerInfo.AssetPath);
|
||||||
rowData.AddStringValueCell("SpawnScene", providerInfo.OriginScene);
|
rowData.AddStringValueCell("SpawnScene", providerInfo.SpawnScene);
|
||||||
rowData.AddStringValueCell("StartTime", providerInfo.StartTime);
|
rowData.AddStringValueCell("StartTime", providerInfo.StartTime);
|
||||||
rowData.AddLongValueCell("LoadingTime", providerInfo.ElapsedMS);
|
rowData.AddLongValueCell("LoadingTime", providerInfo.ElapsedMilliseconds);
|
||||||
rowData.AddLongValueCell("RefCount", providerInfo.ReferenceCount);
|
rowData.AddLongValueCell("RefCount", providerInfo.ReferenceCount);
|
||||||
rowData.AddStringValueCell("Status", providerInfo.Status.ToString());
|
rowData.AddStringValueCell("Status", providerInfo.Status.ToString());
|
||||||
_sourceDatas.Add(rowData);
|
_sourceDatas.Add(rowData);
|
||||||
@@ -378,8 +378,8 @@ namespace YooAsset.Editor
|
|||||||
DiagnosticProviderInfo providerInfo = providerTableData.ProviderInfo;
|
DiagnosticProviderInfo providerInfo = providerTableData.ProviderInfo;
|
||||||
|
|
||||||
// 填充依赖数据
|
// 填充依赖数据
|
||||||
var sourceDatas = new List<ITableData>(providerInfo.DependentBundles.Count);
|
var sourceDatas = new List<ITableData>(providerInfo.Dependencies.Count);
|
||||||
foreach (var bundleName in providerInfo.DependentBundles)
|
foreach (var bundleName in providerInfo.Dependencies)
|
||||||
{
|
{
|
||||||
var dependBundleInfo = packageData.GetBundleInfo(bundleName);
|
var dependBundleInfo = packageData.GetBundleInfo(bundleName);
|
||||||
var rowData = new DependTableData();
|
var rowData = new DependTableData();
|
||||||
|
|||||||
@@ -446,14 +446,14 @@ namespace YooAsset.Editor
|
|||||||
var sourceDatas = new List<ITableData>(1000);
|
var sourceDatas = new List<ITableData>(1000);
|
||||||
foreach (var providerInfo in packageData.ProviderInfos)
|
foreach (var providerInfo in packageData.ProviderInfos)
|
||||||
{
|
{
|
||||||
foreach (var dependBundleName in providerInfo.DependentBundles)
|
foreach (var dependBundleName in providerInfo.Dependencies)
|
||||||
{
|
{
|
||||||
if (dependBundleName == selectBundleInfo.BundleName)
|
if (dependBundleName == selectBundleInfo.BundleName)
|
||||||
{
|
{
|
||||||
var rowData = new UsingTableData();
|
var rowData = new UsingTableData();
|
||||||
rowData.ProviderInfo = providerInfo;
|
rowData.ProviderInfo = providerInfo;
|
||||||
rowData.AddStringValueCell("UsingAssets", providerInfo.AssetPath);
|
rowData.AddStringValueCell("UsingAssets", providerInfo.AssetPath);
|
||||||
rowData.AddStringValueCell("SpawnScene", providerInfo.OriginScene);
|
rowData.AddStringValueCell("SpawnScene", providerInfo.SpawnScene);
|
||||||
rowData.AddStringValueCell("StartTime", providerInfo.StartTime);
|
rowData.AddStringValueCell("StartTime", providerInfo.StartTime);
|
||||||
rowData.AddLongValueCell("RefCount", providerInfo.ReferenceCount);
|
rowData.AddLongValueCell("RefCount", providerInfo.ReferenceCount);
|
||||||
rowData.AddStringValueCell("Status", providerInfo.Status);
|
rowData.AddStringValueCell("Status", providerInfo.Status);
|
||||||
@@ -469,7 +469,7 @@ namespace YooAsset.Editor
|
|||||||
// 填充ReferenceTableView
|
// 填充ReferenceTableView
|
||||||
{
|
{
|
||||||
var sourceDatas = new List<ITableData>(1000);
|
var sourceDatas = new List<ITableData>(1000);
|
||||||
foreach (string referenceBundleName in selectBundleInfo.ReferencedByBundles)
|
foreach (string referenceBundleName in selectBundleInfo.Referencers)
|
||||||
{
|
{
|
||||||
var bundleInfo = packageData.GetBundleInfo(referenceBundleName);
|
var bundleInfo = packageData.GetBundleInfo(referenceBundleName);
|
||||||
var rowData = new ReferenceTableData();
|
var rowData = new ReferenceTableData();
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ namespace YooAsset.Editor
|
|||||||
rowData.AddLongValueCell("Priority", operationInfo.Priority);
|
rowData.AddLongValueCell("Priority", operationInfo.Priority);
|
||||||
rowData.AddDoubleValueCell("Progress", operationInfo.Progress);
|
rowData.AddDoubleValueCell("Progress", operationInfo.Progress);
|
||||||
rowData.AddStringValueCell("StartTime", operationInfo.StartTime);
|
rowData.AddStringValueCell("StartTime", operationInfo.StartTime);
|
||||||
rowData.AddLongValueCell("ElapsedMS", operationInfo.ElapsedMS);
|
rowData.AddLongValueCell("ElapsedMS", operationInfo.ElapsedMilliseconds);
|
||||||
rowData.AddStringValueCell("Status", operationInfo.Status.ToString());
|
rowData.AddStringValueCell("Status", operationInfo.Status.ToString());
|
||||||
rowData.AddStringValueCell("Desc", operationInfo.OperationDesc);
|
rowData.AddStringValueCell("Desc", operationInfo.OperationDesc);
|
||||||
_sourceDatas.Add(rowData);
|
_sourceDatas.Add(rowData);
|
||||||
@@ -474,7 +474,7 @@ namespace YooAsset.Editor
|
|||||||
// ElapsedMS
|
// ElapsedMS
|
||||||
{
|
{
|
||||||
var label = container.Q<Label>("ElapsedMS");
|
var label = container.Q<Label>("ElapsedMS");
|
||||||
label.text = operationInfo.ElapsedMS.ToString();
|
label.text = operationInfo.ElapsedMilliseconds.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status
|
// Status
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ namespace YooAsset
|
|||||||
public abstract class AsyncOperationBase : IEnumerator, IComparable<AsyncOperationBase>
|
public abstract class AsyncOperationBase : IEnumerator, IComparable<AsyncOperationBase>
|
||||||
{
|
{
|
||||||
private List<AsyncOperationBase> _children;
|
private List<AsyncOperationBase> _children;
|
||||||
private Action<AsyncOperationBase> _completedCallbacks;
|
private Action<AsyncOperationBase> _onCompleted;
|
||||||
private uint _priority;
|
private uint _priority;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否正处于同步等待状态
|
/// 是否正处于同步等待状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal bool IsWaitingForAsyncComplete { get; private set; }
|
internal bool IsWaitForCompletion { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标记脏(用于调度器检测并重排)
|
/// 标记脏(用于调度器检测并重排)
|
||||||
@@ -38,9 +38,9 @@ namespace YooAsset
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (IsWaitingForAsyncComplete)
|
if (IsWaitForCompletion)
|
||||||
return false;
|
return false;
|
||||||
return OperationSystem.IsBusy;
|
return AsyncOperationSystem.IsBusy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ namespace YooAsset
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return Status == EOperationStatus.Succeed || Status == EOperationStatus.Failed || Status == EOperationStatus.Aborted;
|
return Status == EOperationStatus.Succeeded || Status == EOperationStatus.Failed || Status == EOperationStatus.Aborted;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +112,12 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_completedCallbacks += value;
|
_onCompleted += value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remove
|
remove
|
||||||
{
|
{
|
||||||
_completedCallbacks -= value;
|
_onCompleted -= value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,9 +160,9 @@ namespace YooAsset
|
|||||||
/// 内部同步等待方法(子类可选实现)
|
/// 内部同步等待方法(子类可选实现)
|
||||||
/// 默认抛出异常,如果异步操作需要支持,子类应重写以支持同步等待
|
/// 默认抛出异常,如果异步操作需要支持,子类应重写以支持同步等待
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal virtual void InternalWaitForAsyncComplete()
|
internal virtual void InternalWaitForCompletion()
|
||||||
{
|
{
|
||||||
throw new YooInternalException($"InternalWaitForAsyncComplete() not implemented : {this.GetType().Name}");
|
throw new YooInternalException($"InternalWaitForCompletion not implemented : {this.GetType().Name}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -321,9 +321,9 @@ namespace YooAsset
|
|||||||
// 结束记录
|
// 结束记录
|
||||||
DebugEndRecording();
|
DebugEndRecording();
|
||||||
|
|
||||||
if (_completedCallbacks != null)
|
if (_onCompleted != null)
|
||||||
{
|
{
|
||||||
var invocationList = _completedCallbacks.GetInvocationList();
|
var invocationList = _onCompleted.GetInvocationList();
|
||||||
foreach (var handler in invocationList)
|
foreach (var handler in invocationList)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -337,7 +337,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_completedCallbacks = null;
|
_onCompleted = null;
|
||||||
if (_taskCompletionSource != null)
|
if (_taskCompletionSource != null)
|
||||||
_taskCompletionSource.TrySetResult(null);
|
_taskCompletionSource.TrySetResult(null);
|
||||||
}
|
}
|
||||||
@@ -346,7 +346,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行一次更新逻辑
|
/// 执行一次更新逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void RunOnceExecution()
|
protected void ExecuteOnce()
|
||||||
{
|
{
|
||||||
if (IsDone)
|
if (IsDone)
|
||||||
return;
|
return;
|
||||||
@@ -361,7 +361,7 @@ namespace YooAsset
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// 用于需要快速完成但又不想完全阻塞主线程的场景。
|
/// 用于需要快速完成但又不想完全阻塞主线程的场景。
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
protected void RunBatchExecution(int count = 1000)
|
protected void ExecuteBatch(int count = 1000)
|
||||||
{
|
{
|
||||||
if (IsDone)
|
if (IsDone)
|
||||||
return;
|
return;
|
||||||
@@ -386,7 +386,7 @@ namespace YooAsset
|
|||||||
/// 注意:该方法会阻塞主线程
|
/// 注意:该方法会阻塞主线程
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sleepMS">休眠时长</param>
|
/// <param name="sleepMS">休眠时长</param>
|
||||||
protected void RunUntilCompletion(int sleepMS = 1)
|
protected void ExecuteUntilComplete(int sleepMS = 1)
|
||||||
{
|
{
|
||||||
if (IsDone)
|
if (IsDone)
|
||||||
return;
|
return;
|
||||||
@@ -405,7 +405,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 同步等待异步执行完毕(会阻塞当前线程)
|
/// 同步等待异步执行完毕(会阻塞当前线程)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void WaitForAsyncComplete()
|
public void WaitForCompletion()
|
||||||
{
|
{
|
||||||
//注意:防止异步操作被挂起陷入无限死循环!
|
//注意:防止异步操作被挂起陷入无限死循环!
|
||||||
if (Status == EOperationStatus.None)
|
if (Status == EOperationStatus.None)
|
||||||
@@ -413,12 +413,12 @@ namespace YooAsset
|
|||||||
StartOperation();
|
StartOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsWaitingForAsyncComplete == false)
|
if (IsWaitForCompletion == false)
|
||||||
{
|
{
|
||||||
IsWaitingForAsyncComplete = true;
|
IsWaitForCompletion = true;
|
||||||
|
|
||||||
if (IsDone == false)
|
if (IsDone == false)
|
||||||
InternalWaitForAsyncComplete();
|
InternalWaitForCompletion();
|
||||||
|
|
||||||
if (IsDone == false)
|
if (IsDone == false)
|
||||||
{
|
{
|
||||||
@@ -441,7 +441,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理耗时(单位:毫秒)
|
/// 处理耗时(单位:毫秒)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long ElapsedMS { get; protected set; }
|
public long ElapsedMilliseconds { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 任务耗时计时器
|
/// 任务耗时计时器
|
||||||
@@ -463,7 +463,7 @@ namespace YooAsset
|
|||||||
{
|
{
|
||||||
if (_stopwatch != null)
|
if (_stopwatch != null)
|
||||||
{
|
{
|
||||||
ElapsedMS = _stopwatch.ElapsedMilliseconds;
|
ElapsedMilliseconds = _stopwatch.ElapsedMilliseconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,7 +472,7 @@ namespace YooAsset
|
|||||||
{
|
{
|
||||||
if (_stopwatch != null)
|
if (_stopwatch != null)
|
||||||
{
|
{
|
||||||
ElapsedMS = _stopwatch.ElapsedMilliseconds;
|
ElapsedMilliseconds = _stopwatch.ElapsedMilliseconds;
|
||||||
_stopwatch = null;
|
_stopwatch = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -535,7 +535,7 @@ namespace YooAsset
|
|||||||
/// 获取调试信息
|
/// 获取调试信息
|
||||||
/// 注意:递归构建子树存在深度风险
|
/// 注意:递归构建子树存在深度风险
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DiagnosticOperationInfo GetDebugOperationInfo()
|
internal DiagnosticOperationInfo GetDiagnosticInfo()
|
||||||
{
|
{
|
||||||
var operationInfo = new DiagnosticOperationInfo();
|
var operationInfo = new DiagnosticOperationInfo();
|
||||||
operationInfo.OperationName = this.GetType().Name;
|
operationInfo.OperationName = this.GetType().Name;
|
||||||
@@ -543,7 +543,7 @@ namespace YooAsset
|
|||||||
operationInfo.Priority = Priority;
|
operationInfo.Priority = Priority;
|
||||||
operationInfo.Progress = Progress;
|
operationInfo.Progress = Progress;
|
||||||
operationInfo.StartTime = StartTime;
|
operationInfo.StartTime = StartTime;
|
||||||
operationInfo.ElapsedMS = ElapsedMS;
|
operationInfo.ElapsedMilliseconds = ElapsedMilliseconds;
|
||||||
operationInfo.Status = Status.ToString();
|
operationInfo.Status = Status.ToString();
|
||||||
|
|
||||||
if (_children == null)
|
if (_children == null)
|
||||||
@@ -555,7 +555,7 @@ namespace YooAsset
|
|||||||
operationInfo.Children = new List<DiagnosticOperationInfo>(_children.Count);
|
operationInfo.Children = new List<DiagnosticOperationInfo>(_children.Count);
|
||||||
foreach (var child in _children)
|
foreach (var child in _children)
|
||||||
{
|
{
|
||||||
var childInfo = child.GetDebugOperationInfo();
|
var childInfo = child.GetDiagnosticInfo();
|
||||||
operationInfo.Children.Add(childInfo);
|
operationInfo.Children.Add(childInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,9 +6,9 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步操作调度器
|
/// 异步操作调度器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class OperationScheduler : IComparable<OperationScheduler>
|
internal class AsyncOperationScheduler : IComparable<AsyncOperationScheduler>
|
||||||
{
|
{
|
||||||
private readonly List<AsyncOperationBase> _operations = new List<AsyncOperationBase>(100);
|
private readonly List<AsyncOperationBase> _runningOperations = new List<AsyncOperationBase>(100);
|
||||||
private readonly List<AsyncOperationBase> _pendingOperations = new List<AsyncOperationBase>(100);
|
private readonly List<AsyncOperationBase> _pendingOperations = new List<AsyncOperationBase>(100);
|
||||||
private uint _priority;
|
private uint _priority;
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ namespace YooAsset
|
|||||||
public int CreationOrder { get; private set; }
|
public int CreationOrder { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
public OperationScheduler(string packageName, int creationOrder)
|
public AsyncOperationScheduler(string packageName, int creationOrder)
|
||||||
{
|
{
|
||||||
PackageName = packageName;
|
PackageName = packageName;
|
||||||
CreationOrder = creationOrder;
|
CreationOrder = creationOrder;
|
||||||
@@ -69,25 +69,25 @@ namespace YooAsset
|
|||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
// 移除已经完成的异步操作
|
// 移除已经完成的异步操作
|
||||||
for (int i = _operations.Count - 1; i >= 0; i--)
|
for (int i = _runningOperations.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var operation = _operations[i];
|
var operation = _runningOperations[i];
|
||||||
if (operation.IsFinished)
|
if (operation.IsFinished)
|
||||||
{
|
{
|
||||||
_operations.RemoveAt(i);
|
_runningOperations.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加新增的异步操作
|
// 添加新增的异步操作
|
||||||
if (_pendingOperations.Count > 0)
|
if (_pendingOperations.Count > 0)
|
||||||
{
|
{
|
||||||
_operations.AddRange(_pendingOperations);
|
_runningOperations.AddRange(_pendingOperations);
|
||||||
_pendingOperations.Clear();
|
_pendingOperations.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测是否需要执行排序
|
// 检测是否需要执行排序
|
||||||
bool isDirty = false;
|
bool isDirty = false;
|
||||||
foreach (var operation in _operations)
|
foreach (var operation in _runningOperations)
|
||||||
{
|
{
|
||||||
if (operation.IsDirty)
|
if (operation.IsDirty)
|
||||||
{
|
{
|
||||||
@@ -97,17 +97,17 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
if (isDirty)
|
if (isDirty)
|
||||||
{
|
{
|
||||||
_operations.Sort();
|
_runningOperations.Sort();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新进行中的异步操作
|
// 更新进行中的异步操作
|
||||||
for (int i = 0; i < _operations.Count; i++)
|
for (int i = 0; i < _runningOperations.Count; i++)
|
||||||
{
|
{
|
||||||
// 检查全局时间切片预算
|
// 检查全局时间切片预算
|
||||||
if (OperationSystem.IsBusy)
|
if (AsyncOperationSystem.IsBusy)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
var operation = _operations[i];
|
var operation = _runningOperations[i];
|
||||||
if (operation.IsFinished)
|
if (operation.IsFinished)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -116,9 +116,9 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 清空并中止所有任务
|
/// 中止所有任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ClearAll()
|
public void AbortAll()
|
||||||
{
|
{
|
||||||
// 终止临时队列里的任务
|
// 终止临时队列里的任务
|
||||||
foreach (var operation in _pendingOperations)
|
foreach (var operation in _pendingOperations)
|
||||||
@@ -128,32 +128,32 @@ namespace YooAsset
|
|||||||
_pendingOperations.Clear();
|
_pendingOperations.Clear();
|
||||||
|
|
||||||
// 终止正在进行的任务
|
// 终止正在进行的任务
|
||||||
foreach (var operation in _operations)
|
foreach (var operation in _runningOperations)
|
||||||
{
|
{
|
||||||
operation.AbortOperation();
|
operation.AbortOperation();
|
||||||
}
|
}
|
||||||
_operations.Clear();
|
_runningOperations.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取调试信息
|
/// 获取调试信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<DiagnosticOperationInfo> GetDebugOperationInfos()
|
public List<DiagnosticOperationInfo> GetDiagnosticInfos()
|
||||||
{
|
{
|
||||||
int totalCount = _operations.Count + _pendingOperations.Count;
|
int totalCount = _runningOperations.Count + _pendingOperations.Count;
|
||||||
List<DiagnosticOperationInfo> result = new List<DiagnosticOperationInfo>(totalCount);
|
List<DiagnosticOperationInfo> result = new List<DiagnosticOperationInfo>(totalCount);
|
||||||
|
|
||||||
// 包含正在执行的任务
|
// 包含正在执行的任务
|
||||||
foreach (var operation in _operations)
|
foreach (var operation in _runningOperations)
|
||||||
{
|
{
|
||||||
var operationInfo = operation.GetDebugOperationInfo();
|
var operationInfo = operation.GetDiagnosticInfo();
|
||||||
result.Add(operationInfo);
|
result.Add(operationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 包含待处理的新任务
|
// 包含待处理的新任务
|
||||||
foreach (var operation in _pendingOperations)
|
foreach (var operation in _pendingOperations)
|
||||||
{
|
{
|
||||||
var operationInfo = operation.GetDebugOperationInfo();
|
var operationInfo = operation.GetDiagnosticInfo();
|
||||||
result.Add(operationInfo);
|
result.Add(operationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 排序接口实现
|
#region 排序接口实现
|
||||||
public int CompareTo(OperationScheduler other)
|
public int CompareTo(AsyncOperationScheduler other)
|
||||||
{
|
{
|
||||||
// 优先级高的排前面
|
// 优先级高的排前面
|
||||||
int result = other.Priority.CompareTo(this.Priority);
|
int result = other.Priority.CompareTo(this.Priority);
|
||||||
@@ -9,7 +9,7 @@ namespace YooAsset
|
|||||||
/// 异步操作系统(静态调度器)
|
/// 异步操作系统(静态调度器)
|
||||||
/// 负责管理所有包裹的调度器,提供时间切片执行机制
|
/// 负责管理所有包裹的调度器,提供时间切片执行机制
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class OperationSystem
|
internal static class AsyncOperationSystem
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
|
[UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
@@ -22,14 +22,14 @@ namespace YooAsset
|
|||||||
public const string GlobalSchedulerName = "YOOASSET_GLOBAL_SCHEDULER"; // 全局调度器名称
|
public const string GlobalSchedulerName = "YOOASSET_GLOBAL_SCHEDULER"; // 全局调度器名称
|
||||||
private const long MinTimeSlice = 10; // 最小时间片(毫秒)
|
private const long MinTimeSlice = 10; // 最小时间片(毫秒)
|
||||||
|
|
||||||
private static readonly Dictionary<string, OperationScheduler> _schedulerDict = new Dictionary<string, OperationScheduler>(100);
|
private static readonly Dictionary<string, AsyncOperationScheduler> _schedulerDict = new Dictionary<string, AsyncOperationScheduler>(100);
|
||||||
private static readonly List<OperationScheduler> _schedulerList = new List<OperationScheduler>(100);
|
private static readonly List<AsyncOperationScheduler> _schedulerList = new List<AsyncOperationScheduler>(100);
|
||||||
private static bool _isInitialized;
|
private static bool _isInitialized;
|
||||||
private static int _nextCreationIndex;
|
private static int _nextCreationOrder;
|
||||||
|
|
||||||
// 计时器相关
|
// 计时器相关
|
||||||
private static Stopwatch _systemStopwatch;
|
private static Stopwatch _stopwatch;
|
||||||
private static long _frameTime;
|
private static long _frameStartTime;
|
||||||
private static long _maxTimeSlice = long.MaxValue;
|
private static long _maxTimeSlice = long.MaxValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -62,14 +62,14 @@ namespace YooAsset
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_systemStopwatch == null)
|
if (_stopwatch == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (_maxTimeSlice == long.MaxValue)
|
if (_maxTimeSlice == long.MaxValue)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// 注意 : 单次调用开销约1微秒
|
// 注意 : 单次调用开销约1微秒
|
||||||
return _systemStopwatch.ElapsedMilliseconds - _frameTime >= _maxTimeSlice;
|
return _stopwatch.ElapsedMilliseconds - _frameStartTime >= _maxTimeSlice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
_systemStopwatch = Stopwatch.StartNew();
|
_stopwatch = Stopwatch.StartNew();
|
||||||
|
|
||||||
// 创建全局调度器
|
// 创建全局调度器
|
||||||
CreatePackageScheduler(GlobalSchedulerName, uint.MaxValue);
|
CreatePackageScheduler(GlobalSchedulerName, uint.MaxValue);
|
||||||
@@ -116,7 +116,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新帧时间
|
// 更新帧时间
|
||||||
_frameTime = _systemStopwatch.ElapsedMilliseconds;
|
_frameStartTime = _stopwatch.ElapsedMilliseconds;
|
||||||
|
|
||||||
// 更新调度器
|
// 更新调度器
|
||||||
for (int i = 0; i < _schedulerList.Count; i++)
|
for (int i = 0; i < _schedulerList.Count; i++)
|
||||||
@@ -138,30 +138,30 @@ namespace YooAsset
|
|||||||
// 清空所有调度器
|
// 清空所有调度器
|
||||||
foreach (var scheduler in _schedulerList)
|
foreach (var scheduler in _schedulerList)
|
||||||
{
|
{
|
||||||
scheduler.ClearAll();
|
scheduler.AbortAll();
|
||||||
}
|
}
|
||||||
_schedulerDict.Clear();
|
_schedulerDict.Clear();
|
||||||
_schedulerList.Clear();
|
_schedulerList.Clear();
|
||||||
_nextCreationIndex = 0;
|
_nextCreationOrder = 0;
|
||||||
|
|
||||||
_systemStopwatch = null;
|
_stopwatch = null;
|
||||||
_frameTime = 0;
|
_frameStartTime = 0;
|
||||||
_maxTimeSlice = long.MaxValue;
|
_maxTimeSlice = long.MaxValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建包裹调度器
|
/// 创建包裹调度器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static OperationScheduler CreatePackageScheduler(string packageName, uint priority)
|
public static AsyncOperationScheduler CreatePackageScheduler(string packageName, uint priority)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
if (_schedulerDict.ContainsKey(packageName))
|
if (_schedulerDict.ContainsKey(packageName))
|
||||||
{
|
{
|
||||||
throw new YooInternalException($"Package scheduler already exists: {packageName}");
|
throw new YooInternalException($"Package scheduler already exists: {packageName}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduler = new OperationScheduler(packageName, _nextCreationIndex++);
|
var scheduler = new AsyncOperationScheduler(packageName, _nextCreationOrder++);
|
||||||
_schedulerDict.Add(packageName, scheduler);
|
_schedulerDict.Add(packageName, scheduler);
|
||||||
_schedulerList.Add(scheduler);
|
_schedulerList.Add(scheduler);
|
||||||
scheduler.Priority = priority;
|
scheduler.Priority = priority;
|
||||||
@@ -173,7 +173,7 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void DestroyPackageScheduler(string packageName)
|
public static void DestroyPackageScheduler(string packageName)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
// 不允许销毁默认调度器
|
// 不允许销毁默认调度器
|
||||||
if (packageName == GlobalSchedulerName)
|
if (packageName == GlobalSchedulerName)
|
||||||
@@ -183,7 +183,7 @@ namespace YooAsset
|
|||||||
|
|
||||||
if (_schedulerDict.TryGetValue(packageName, out var scheduler))
|
if (_schedulerDict.TryGetValue(packageName, out var scheduler))
|
||||||
{
|
{
|
||||||
scheduler.ClearAll();
|
scheduler.AbortAll();
|
||||||
_schedulerDict.Remove(packageName);
|
_schedulerDict.Remove(packageName);
|
||||||
_schedulerList.Remove(scheduler);
|
_schedulerList.Remove(scheduler);
|
||||||
}
|
}
|
||||||
@@ -194,10 +194,10 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void ClearPackageOperations(string packageName)
|
public static void ClearPackageOperations(string packageName)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
var scheduler = GetScheduler(packageName);
|
var scheduler = GetScheduler(packageName);
|
||||||
scheduler.ClearAll();
|
scheduler.AbortAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -205,7 +205,7 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void StartOperation(string packageName, AsyncOperationBase operation)
|
public static void StartOperation(string packageName, AsyncOperationBase operation)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
var scheduler = GetScheduler(packageName);
|
var scheduler = GetScheduler(packageName);
|
||||||
scheduler.StartOperation(operation);
|
scheduler.StartOperation(operation);
|
||||||
@@ -216,7 +216,7 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static void SetSchedulerPriority(string packageName, uint priority)
|
public static void SetSchedulerPriority(string packageName, uint priority)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
var scheduler = GetScheduler(packageName);
|
var scheduler = GetScheduler(packageName);
|
||||||
scheduler.Priority = priority;
|
scheduler.Priority = priority;
|
||||||
@@ -227,7 +227,7 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static uint GetSchedulerPriority(string packageName)
|
public static uint GetSchedulerPriority(string packageName)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
var scheduler = GetScheduler(packageName);
|
var scheduler = GetScheduler(packageName);
|
||||||
return scheduler.Priority;
|
return scheduler.Priority;
|
||||||
@@ -236,7 +236,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取调度器(严格模式)
|
/// 获取调度器(严格模式)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static OperationScheduler GetScheduler(string packageName)
|
private static AsyncOperationScheduler GetScheduler(string packageName)
|
||||||
{
|
{
|
||||||
if (_schedulerDict.TryGetValue(packageName, out var scheduler))
|
if (_schedulerDict.TryGetValue(packageName, out var scheduler))
|
||||||
{
|
{
|
||||||
@@ -248,24 +248,24 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 调试信息
|
#region 调试信息
|
||||||
internal static List<DiagnosticOperationInfo> GetDebugOperationInfos(string packageName)
|
internal static List<DiagnosticOperationInfo> GetDiagnosticInfos(string packageName)
|
||||||
{
|
{
|
||||||
DebugEnsureInitialized(packageName);
|
DebugCheckInitialized(packageName);
|
||||||
|
|
||||||
var scheduler = GetScheduler(packageName);
|
var scheduler = GetScheduler(packageName);
|
||||||
return scheduler.GetDebugOperationInfos();
|
return scheduler.GetDiagnosticInfos();
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 调试方法
|
#region 调试方法
|
||||||
[Conditional("DEBUG")]
|
[Conditional("DEBUG")]
|
||||||
private static void DebugEnsureInitialized(string packageName)
|
private static void DebugCheckInitialized(string packageName)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(packageName))
|
if (string.IsNullOrWhiteSpace(packageName))
|
||||||
throw new YooInternalException("Package name is null or empty.");
|
throw new YooInternalException("Package name is null or empty.");
|
||||||
|
|
||||||
if (_isInitialized == false)
|
if (_isInitialized == false)
|
||||||
throw new YooInternalException($"{nameof(OperationSystem)} not initialized.");
|
throw new YooInternalException($"{nameof(AsyncOperationSystem)} not initialized.");
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已成功
|
/// 已成功
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Succeed,
|
Succeeded,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 已失败
|
/// 已失败
|
||||||
@@ -5,10 +5,10 @@ using UnityEngine.Networking.PlayerConnection;
|
|||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 远程调试行为组件
|
/// 诊断行为组件
|
||||||
/// 负责接收 Editor 命令并发送诊断数据
|
/// 负责接收 Editor 命令并发送诊断数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class RemoteDebugBehaviour : MonoBehaviour
|
internal class DiagnosticBehaviour : MonoBehaviour
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||||
@@ -62,13 +62,13 @@ namespace YooAsset
|
|||||||
|
|
||||||
private static void HandleEditorMessage(MessageEventArgs args)
|
private static void HandleEditorMessage(MessageEventArgs args)
|
||||||
{
|
{
|
||||||
var command = RemoteDebugCommand.Deserialize(args.data);
|
var command = DiagnosticCommand.Deserialize(args.data);
|
||||||
YooLogger.Log($"Handle remote command: {command.CommandType} Param: {command.Parameter}");
|
YooLogger.Log($"[{nameof(DiagnosticBehaviour)}] Handle command: Type={command.CommandType}, Param={command.Parameter}");
|
||||||
if (command.CommandType == (int)EDebugCommandType.SampleOnce)
|
if (command.CommandType == (int)EDiagnosticCommandType.SampleOnce)
|
||||||
{
|
{
|
||||||
_sampleOnce = true;
|
_sampleOnce = true;
|
||||||
}
|
}
|
||||||
else if (command.CommandType == (int)EDebugCommandType.AutoSampling)
|
else if (command.CommandType == (int)EDiagnosticCommandType.AutoSampling)
|
||||||
{
|
{
|
||||||
if (command.Parameter == "open")
|
if (command.Parameter == "open")
|
||||||
_autoSampling = true;
|
_autoSampling = true;
|
||||||
@@ -77,7 +77,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotImplementedException(command.CommandType.ToString());
|
throw new NotImplementedException($"Unknown diagnostic command type: {command.CommandType}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,11 @@ using UnityEngine;
|
|||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 远程调试命令
|
/// 诊断命令
|
||||||
/// 用于 Editor 向 Player 发送调试指令
|
/// 用于 Editor 向 Player 发送诊断指令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable]
|
[Serializable]
|
||||||
internal class RemoteDebugCommand
|
internal class DiagnosticCommand
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 命令类型
|
/// 命令类型
|
||||||
@@ -25,9 +25,9 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 序列化命令为字节数组
|
/// 序列化命令为字节数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="command">要序列化的远程调试命令</param>
|
/// <param name="command">要序列化的诊断命令</param>
|
||||||
/// <returns>UTF-8 编码的 JSON 字节数组</returns>
|
/// <returns>UTF-8 编码的 JSON 字节数组</returns>
|
||||||
public static byte[] Serialize(RemoteDebugCommand command)
|
public static byte[] Serialize(DiagnosticCommand command)
|
||||||
{
|
{
|
||||||
return Encoding.UTF8.GetBytes(JsonUtility.ToJson(command));
|
return Encoding.UTF8.GetBytes(JsonUtility.ToJson(command));
|
||||||
}
|
}
|
||||||
@@ -36,10 +36,10 @@ namespace YooAsset
|
|||||||
/// 从字节数组反序列化命令
|
/// 从字节数组反序列化命令
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="data">UTF-8 编码的 JSON 字节数组</param>
|
/// <param name="data">UTF-8 编码的 JSON 字节数组</param>
|
||||||
/// <returns>反序列化后的远程调试命令</returns>
|
/// <returns>反序列化后的诊断命令</returns>
|
||||||
public static RemoteDebugCommand Deserialize(byte[] data)
|
public static DiagnosticCommand Deserialize(byte[] data)
|
||||||
{
|
{
|
||||||
return JsonUtility.FromJson<RemoteDebugCommand>(Encoding.UTF8.GetString(data));
|
return JsonUtility.FromJson<DiagnosticCommand>(Encoding.UTF8.GetString(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2c65d3ba8da65004b8d36dbeecc6c6be
|
guid: c137052d50eadfa4caf6b2157ebeeaad
|
||||||
TextScriptImporter:
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
@@ -26,9 +26,9 @@ namespace YooAsset
|
|||||||
public string Status;
|
public string Status;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 该资源包被谁引用
|
/// 引用该资源包的其他资源包列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> ReferencedByBundles;
|
public List<string> Referencers;
|
||||||
|
|
||||||
public int CompareTo(DiagnosticBundleInfo other)
|
public int CompareTo(DiagnosticBundleInfo other)
|
||||||
{
|
{
|
||||||
@@ -33,7 +33,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理耗时(单位:毫秒)
|
/// 处理耗时(单位:毫秒)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long ElapsedMS;
|
public long ElapsedMilliseconds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步操作的执行进度
|
/// 异步操作的执行进度
|
||||||
@@ -10,6 +10,9 @@ namespace YooAsset
|
|||||||
[Serializable]
|
[Serializable]
|
||||||
internal class DiagnosticPackageData
|
internal class DiagnosticPackageData
|
||||||
{
|
{
|
||||||
|
private readonly Dictionary<string, DiagnosticBundleInfo> _bundleInfoDict = new Dictionary<string, DiagnosticBundleInfo>();
|
||||||
|
private bool _isParsed = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 包裹名称
|
/// 包裹名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -30,15 +33,11 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<DiagnosticOperationInfo> OperationInfos = new List<DiagnosticOperationInfo>(1000);
|
public List<DiagnosticOperationInfo> OperationInfos = new List<DiagnosticOperationInfo>(1000);
|
||||||
|
|
||||||
private readonly Dictionary<string, DiagnosticBundleInfo> _bundleInfoDict = new Dictionary<string, DiagnosticBundleInfo>();
|
|
||||||
private bool _isParsed = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取资源包的诊断信息
|
/// 获取资源包的诊断信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DiagnosticBundleInfo GetBundleInfo(string bundleName)
|
public DiagnosticBundleInfo GetBundleInfo(string bundleName)
|
||||||
{
|
{
|
||||||
// 解析数据
|
|
||||||
if (_isParsed == false)
|
if (_isParsed == false)
|
||||||
{
|
{
|
||||||
_isParsed = true;
|
_isParsed = true;
|
||||||
@@ -57,7 +56,7 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.LogError($"Cannot find {nameof(DiagnosticBundleInfo)} : {bundleName}");
|
YooLogger.Error($"[{nameof(DiagnosticPackageData)}] Bundle info not found: {bundleName}");
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -16,10 +16,10 @@ namespace YooAsset
|
|||||||
public string AssetPath;
|
public string AssetPath;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源加载时所在的场景
|
/// 资源加载时的活跃场景
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string OriginScene;
|
public string SpawnScene;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源加载开始时间
|
/// 资源加载开始时间
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -28,7 +28,7 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加载耗时(单位:毫秒)
|
/// 加载耗时(单位:毫秒)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public long ElapsedMS;
|
public long ElapsedMilliseconds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 引用计数
|
/// 引用计数
|
||||||
@@ -41,9 +41,9 @@ namespace YooAsset
|
|||||||
public string Status;
|
public string Status;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 依赖的资源包列表
|
/// 资源依赖的资源包列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<string> DependentBundles;
|
public List<string> Dependencies;
|
||||||
|
|
||||||
public int CompareTo(DiagnosticProviderInfo other)
|
public int CompareTo(DiagnosticProviderInfo other)
|
||||||
{
|
{
|
||||||
@@ -13,9 +13,9 @@ namespace YooAsset
|
|||||||
internal class DiagnosticReport
|
internal class DiagnosticReport
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调试器版本
|
/// 通信协议版本
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string DebuggerVersion = DiagnosticSystemDefine.DebuggerVersion;
|
public string ProtocolVersion;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 报告发生的游戏帧
|
/// 报告发生的游戏帧
|
||||||
@@ -27,6 +27,18 @@ namespace YooAsset
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public List<DiagnosticPackageData> PackageDataList = new List<DiagnosticPackageData>(10);
|
public List<DiagnosticPackageData> PackageDataList = new List<DiagnosticPackageData>(10);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建新的诊断报告
|
||||||
|
/// </summary>
|
||||||
|
public static DiagnosticReport Create()
|
||||||
|
{
|
||||||
|
var report = new DiagnosticReport();
|
||||||
|
report.ProtocolVersion = DiagnosticSystemDefine.ProtocolVersion;
|
||||||
|
report.FrameCount = Time.frameCount;
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 序列化诊断报告为字节数组
|
/// 序列化诊断报告为字节数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -8,9 +8,9 @@ namespace YooAsset
|
|||||||
internal class DiagnosticSystemDefine
|
internal class DiagnosticSystemDefine
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 调试器版本号
|
/// 通信协议版本号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string DebuggerVersion = "1.0";
|
public const string ProtocolVersion = "1.0";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Player 向 Editor 发送消息的标识符
|
/// Player 向 Editor 发送消息的标识符
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 远程调试命令类型
|
/// 诊断命令类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal enum EDebugCommandType
|
internal enum EDiagnosticCommandType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 采样一次
|
/// 采样一次
|
||||||
@@ -53,7 +53,7 @@ namespace YooAsset
|
|||||||
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
|
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
|
||||||
{
|
{
|
||||||
if (messageID == Guid.Empty)
|
if (messageID == Guid.Empty)
|
||||||
throw new ArgumentException("messageID is empty.");
|
throw new ArgumentException("Message ID cannot be empty.", nameof(messageID));
|
||||||
|
|
||||||
if (_messageHandlers.ContainsKey(messageID) == false)
|
if (_messageHandlers.ContainsKey(messageID) == false)
|
||||||
_messageHandlers.Add(messageID, callback);
|
_messageHandlers.Add(messageID, callback);
|
||||||
@@ -77,7 +77,7 @@ namespace YooAsset
|
|||||||
public void Send(Guid messageID, byte[] data)
|
public void Send(Guid messageID, byte[] data)
|
||||||
{
|
{
|
||||||
if (messageID == Guid.Empty)
|
if (messageID == Guid.Empty)
|
||||||
throw new ArgumentException("messageID is empty.");
|
throw new ArgumentException("Message ID cannot be empty.", nameof(messageID));
|
||||||
|
|
||||||
// 接收对方的消息
|
// 接收对方的消息
|
||||||
MockPlayerConnection.Instance.HandleEditorMessage(messageID, data);
|
MockPlayerConnection.Instance.HandleEditorMessage(messageID, data);
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ namespace YooAsset
|
|||||||
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
|
public void Register(Guid messageID, UnityAction<MessageEventArgs> callback)
|
||||||
{
|
{
|
||||||
if (messageID == Guid.Empty)
|
if (messageID == Guid.Empty)
|
||||||
throw new ArgumentException("messageID is empty.");
|
throw new ArgumentException("Message ID cannot be empty.", nameof(messageID));
|
||||||
|
|
||||||
if (_messageHandlers.ContainsKey(messageID) == false)
|
if (_messageHandlers.ContainsKey(messageID) == false)
|
||||||
_messageHandlers.Add(messageID, callback);
|
_messageHandlers.Add(messageID, callback);
|
||||||
@@ -77,7 +77,7 @@ namespace YooAsset
|
|||||||
public void Send(Guid messageID, byte[] data)
|
public void Send(Guid messageID, byte[] data)
|
||||||
{
|
{
|
||||||
if (messageID == Guid.Empty)
|
if (messageID == Guid.Empty)
|
||||||
throw new ArgumentException("messageID is empty.");
|
throw new ArgumentException("Message ID cannot be empty.", nameof(messageID));
|
||||||
|
|
||||||
// 接收对方的消息
|
// 接收对方的消息
|
||||||
MockEditorConnection.Instance.HandlePlayerMessage(messageID, data);
|
MockEditorConnection.Instance.HandlePlayerMessage(messageID, data);
|
||||||
|
|||||||
@@ -1,478 +0,0 @@
|
|||||||
# DiagnosticSystem 诊断模块
|
|
||||||
|
|
||||||
## 模块概述
|
|
||||||
|
|
||||||
DiagnosticSystem 是 YooAsset 资源管理系统的**运行时诊断模块**,负责收集和传输资源加载的实时状态信息。该模块支持 Editor 与运行时 Player 之间的双向通信,为 AssetBundleDebugger 窗口提供数据支持。
|
|
||||||
|
|
||||||
### 可见性说明
|
|
||||||
|
|
||||||
DiagnosticSystem 属于 YooAsset Runtime 的内部基础模块,目录内大多数类型为 `internal`(仅供 YooAsset Runtime 内部程序集使用)。业务层建议通过 `YooAssets.GetDebugReport()` 等上层接口获取诊断数据,避免直接依赖本模块的内部类型。
|
|
||||||
|
|
||||||
### 核心职责
|
|
||||||
|
|
||||||
- 资源加载状态采集
|
|
||||||
- Provider/Bundle/Operation 诊断信息汇总
|
|
||||||
- Editor 与 Player 的远程通信
|
|
||||||
- 诊断数据的序列化与传输
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 边界与上层协作
|
|
||||||
|
|
||||||
DiagnosticSystem 的职责是提供"诊断数据采集 + 远程通信 + 序列化传输"的基础能力:
|
|
||||||
|
|
||||||
- **本模块不负责数据展示**:数据展示由 Editor 端的 AssetBundleDebugger 窗口实现。
|
|
||||||
- **本模块不负责数据持久化**:诊断数据为实时采集,不进行本地存储。
|
|
||||||
- **本模块不负责数据分析**:数据分析和统计由上层调试工具实现。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 设计目标
|
|
||||||
|
|
||||||
| 目标 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **实时性** | 支持单次采样和持续采样两种模式 |
|
|
||||||
| **低侵入** | 仅在需要时采集数据,不影响正常运行性能 |
|
|
||||||
| **跨平台** | 支持 Editor 模拟和真机远程调试 |
|
|
||||||
| **可扩展性** | 诊断数据结构支持版本控制 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 架构概念
|
|
||||||
|
|
||||||
### 分层架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ AssetBundleDebugger (Editor) │
|
|
||||||
│ (数据展示层) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ MockEditorConnection (Editor) │
|
|
||||||
│ EditorConnection (Runtime) │
|
|
||||||
│ (通信层) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│ 双向消息
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ MockPlayerConnection (Editor) │
|
|
||||||
│ PlayerConnection (Runtime) │
|
|
||||||
│ (通信层) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ RemoteDebugBehaviour │
|
|
||||||
│ (运行时组件) │
|
|
||||||
│ 接收命令、采集数据、发送报告 │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ DiagnosticReport │
|
|
||||||
│ (数据模型) │
|
|
||||||
│ DiagnosticPackageData / ProviderInfo / BundleInfo │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 核心组件
|
|
||||||
|
|
||||||
- **数据模型层**: 定义诊断数据的结构(DiagnosticReport、DiagnosticPackageData 等)
|
|
||||||
- **通信层**: 处理 Editor 与 Player 之间的消息传递
|
|
||||||
- **行为组件层**: 运行时 MonoBehaviour,负责采集和发送诊断数据
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
DiagnosticSystem/
|
|
||||||
├── DiagnosticBundleInfo.cs # 资源包诊断信息
|
|
||||||
├── DiagnosticOperationInfo.cs # 异步操作诊断信息
|
|
||||||
├── DiagnosticPackageData.cs # 包裹诊断数据容器
|
|
||||||
├── DiagnosticProviderInfo.cs # 资源加载诊断信息
|
|
||||||
├── DiagnosticReport.cs # 诊断报告(顶层数据结构)
|
|
||||||
│
|
|
||||||
├── DiagnosticSystemDefine.cs # 诊断系统常量定义
|
|
||||||
├── EDebugCommandType.cs # 调试命令类型枚举
|
|
||||||
├── RemoteDebugCommand.cs # 远程调试命令
|
|
||||||
│
|
|
||||||
├── RemoteDebugBehaviour.cs # 运行时调试组件
|
|
||||||
├── MockEditorConnection.cs # 模拟 Editor 连接(Editor 用)
|
|
||||||
└── MockPlayerConnection.cs # 模拟 Player 连接(Editor 用)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 数据模型
|
|
||||||
|
|
||||||
### DiagnosticReport(诊断报告)
|
|
||||||
|
|
||||||
顶层数据结构,包含所有包裹的诊断数据。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal class DiagnosticReport
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 调试器版本
|
|
||||||
/// </summary>
|
|
||||||
public string DebuggerVersion;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 报告发生的游戏帧
|
|
||||||
/// </summary>
|
|
||||||
public int FrameCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 包裹数据列表
|
|
||||||
/// </summary>
|
|
||||||
public List<DiagnosticPackageData> PackageDataList;
|
|
||||||
|
|
||||||
// 序列化/反序列化方法
|
|
||||||
public static byte[] Serialize(DiagnosticReport report);
|
|
||||||
public static DiagnosticReport Deserialize(byte[] data);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### DiagnosticPackageData(包裹诊断数据)
|
|
||||||
|
|
||||||
单个包裹的诊断数据容器。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal class DiagnosticPackageData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 包裹名称
|
|
||||||
/// </summary>
|
|
||||||
public string PackageName;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 资源加载的诊断信息列表
|
|
||||||
/// </summary>
|
|
||||||
public List<DiagnosticProviderInfo> ProviderInfos;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 资源包的诊断信息列表
|
|
||||||
/// </summary>
|
|
||||||
public List<DiagnosticBundleInfo> BundleInfos;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异步操作的诊断信息列表
|
|
||||||
/// </summary>
|
|
||||||
public List<DiagnosticOperationInfo> OperationInfos;
|
|
||||||
|
|
||||||
// 快速查找方法
|
|
||||||
public DiagnosticBundleInfo GetBundleInfo(string bundleName);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### DiagnosticProviderInfo(资源加载诊断信息)
|
|
||||||
|
|
||||||
描述单个资源加载操作的状态。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal struct DiagnosticProviderInfo
|
|
||||||
{
|
|
||||||
public string AssetPath; // 资源对象路径
|
|
||||||
public string OriginScene; // 资源加载时所在的场景
|
|
||||||
public string StartTime; // 资源加载开始时间
|
|
||||||
public long ElapsedMS; // 加载耗时(毫秒)
|
|
||||||
public int ReferenceCount; // 引用计数
|
|
||||||
public string Status; // 资源的加载状态
|
|
||||||
public List<string> DependentBundles; // 依赖的资源包列表
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### DiagnosticBundleInfo(资源包诊断信息)
|
|
||||||
|
|
||||||
描述单个 AssetBundle 的状态。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal struct DiagnosticBundleInfo
|
|
||||||
{
|
|
||||||
public string BundleName; // 资源包名称
|
|
||||||
public int ReferenceCount; // 引用计数
|
|
||||||
public string Status; // 资源包的加载状态
|
|
||||||
public List<string> ReferencedByBundles; // 该资源包被谁引用
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### DiagnosticOperationInfo(异步操作诊断信息)
|
|
||||||
|
|
||||||
描述异步操作的执行状态。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal struct DiagnosticOperationInfo
|
|
||||||
{
|
|
||||||
public string OperationName; // 异步操作的名称
|
|
||||||
public string OperationDesc; // 异步操作的说明
|
|
||||||
public uint Priority; // 异步操作的优先级
|
|
||||||
public string StartTime; // 开始的时间
|
|
||||||
public long ElapsedMS; // 处理耗时(毫秒)
|
|
||||||
public float Progress; // 异步操作的执行进度
|
|
||||||
public string Status; // 异步操作的执行状态
|
|
||||||
public List<DiagnosticOperationInfo> Children; // 子任务列表
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 通信协议
|
|
||||||
|
|
||||||
### 命令类型
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
/// <summary>
|
|
||||||
/// 远程调试命令类型
|
|
||||||
/// </summary>
|
|
||||||
internal enum EDebugCommandType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 采样一次
|
|
||||||
/// </summary>
|
|
||||||
SampleOnce = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 持续采样
|
|
||||||
/// </summary>
|
|
||||||
AutoSampling = 1,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### RemoteDebugCommand(远程调试命令)
|
|
||||||
|
|
||||||
Editor 向 Player 发送的调试指令。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
[Serializable]
|
|
||||||
internal class RemoteDebugCommand
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 命令类型
|
|
||||||
/// </summary>
|
|
||||||
public int CommandType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 命令附加参数
|
|
||||||
/// </summary>
|
|
||||||
public string Parameter;
|
|
||||||
|
|
||||||
// 序列化/反序列化方法
|
|
||||||
public static byte[] Serialize(RemoteDebugCommand command);
|
|
||||||
public static RemoteDebugCommand Deserialize(byte[] data);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 消息标识符
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal class DiagnosticSystemDefine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 调试器版本号
|
|
||||||
/// </summary>
|
|
||||||
public const string DebuggerVersion = "1.0";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Player 向 Editor 发送消息的标识符
|
|
||||||
/// </summary>
|
|
||||||
public static readonly Guid PlayerToEditorMessageId;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Editor 向 Player 发送消息的标识符
|
|
||||||
/// </summary>
|
|
||||||
public static readonly Guid EditorToPlayerMessageId;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心类说明
|
|
||||||
|
|
||||||
### RemoteDebugBehaviour
|
|
||||||
|
|
||||||
运行时调试组件,负责接收 Editor 命令并发送诊断数据。
|
|
||||||
|
|
||||||
**职责:**
|
|
||||||
- 监听 Editor 发送的调试命令
|
|
||||||
- 根据命令采集诊断数据
|
|
||||||
- 将诊断报告发送回 Editor
|
|
||||||
|
|
||||||
**采样模式:**
|
|
||||||
- `SampleOnce`: 单次采样,采集一帧数据后停止
|
|
||||||
- `AutoSampling`: 持续采样,每帧自动采集并发送数据
|
|
||||||
|
|
||||||
**生命周期:**
|
|
||||||
|
|
||||||
```
|
|
||||||
Awake() ──► 初始化连接
|
|
||||||
OnEnable() ──► 注册消息处理器
|
|
||||||
LateUpdate()──► 检查采样标志,采集并发送数据
|
|
||||||
OnDisable() ──► 注销消息处理器
|
|
||||||
```
|
|
||||||
|
|
||||||
### MockEditorConnection / MockPlayerConnection
|
|
||||||
|
|
||||||
Editor 模式下的模拟连接,用于本地调试。
|
|
||||||
|
|
||||||
**特性:**
|
|
||||||
- 在 Editor 中模拟 `EditorConnection` 和 `PlayerConnection` 的行为
|
|
||||||
- 实现消息的本地传递,无需真机连接
|
|
||||||
- 支持消息注册、注销和发送
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// 注册消息处理器
|
|
||||||
MockPlayerConnection.Instance.Register(messageId, callback);
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
MockPlayerConnection.Instance.Send(messageId, data);
|
|
||||||
|
|
||||||
// 注销消息处理器
|
|
||||||
MockPlayerConnection.Instance.Unregister(messageId);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 通信流程
|
|
||||||
|
|
||||||
### Editor 模式(本地调试)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────────┐ ┌───────────────────┐
|
|
||||||
│ Debugger Window │ │ RemoteDebugBehaviour│
|
|
||||||
│ (Editor) │ │ (PlayMode) │
|
|
||||||
└────────┬──────────┘ └────────┬──────────┘
|
|
||||||
│ │
|
|
||||||
│ 1. 发送采样命令 │
|
|
||||||
│ ─────────────────────────────►
|
|
||||||
│ MockEditorConnection.Send() │
|
|
||||||
│ │
|
|
||||||
│ │ 2. 接收命令
|
|
||||||
│ │ HandleEditorMessage()
|
|
||||||
│ │
|
|
||||||
│ │ 3. 采集诊断数据
|
|
||||||
│ │ YooAssets.GetDebugReport()
|
|
||||||
│ │
|
|
||||||
│ 4. 返回诊断报告 │
|
|
||||||
│ ◄─────────────────────────────
|
|
||||||
│ MockPlayerConnection.Send() │
|
|
||||||
│ │
|
|
||||||
│ 5. 解析并展示数据 │
|
|
||||||
▼ ▼
|
|
||||||
```
|
|
||||||
|
|
||||||
### 真机模式(远程调试)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌───────────────────┐ ┌───────────────────┐
|
|
||||||
│ Debugger Window │ │ RemoteDebugBehaviour│
|
|
||||||
│ (Editor) │ USB │ (Device) │
|
|
||||||
└────────┬──────────┘ ════ └────────┬──────────┘
|
|
||||||
│ │
|
|
||||||
│ 1. 发送采样命令 │
|
|
||||||
│ ─────────────────────────────►
|
|
||||||
│ EditorConnection.Send() │
|
|
||||||
│ │
|
|
||||||
│ │ 2. 接收命令
|
|
||||||
│ │ PlayerConnection.Register()
|
|
||||||
│ │
|
|
||||||
│ │ 3. 采集诊断数据
|
|
||||||
│ │
|
|
||||||
│ 4. 返回诊断报告 │
|
|
||||||
│ ◄─────────────────────────────
|
|
||||||
│ PlayerConnection.Send() │
|
|
||||||
│ │
|
|
||||||
│ 5. 解析并展示数据 │
|
|
||||||
▼ ▼
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 设计模式
|
|
||||||
|
|
||||||
### 单例模式
|
|
||||||
|
|
||||||
`MockEditorConnection` 和 `MockPlayerConnection` 使用单例模式:
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
public static MockPlayerConnection Instance
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_instance == null)
|
|
||||||
_instance = new MockPlayerConnection();
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 观察者模式
|
|
||||||
|
|
||||||
通过消息注册机制实现观察者模式:
|
|
||||||
|
|
||||||
```
|
|
||||||
Register(messageId, callback) ──► 订阅消息
|
|
||||||
Unregister(messageId) ──► 取消订阅
|
|
||||||
HandleXxxMessage(messageId, data) ──► 通知订阅者
|
|
||||||
```
|
|
||||||
|
|
||||||
### 命令模式
|
|
||||||
|
|
||||||
`RemoteDebugCommand` 封装调试指令:
|
|
||||||
|
|
||||||
```
|
|
||||||
Editor Player
|
|
||||||
│ │
|
|
||||||
│ RemoteDebugCommand │
|
|
||||||
│ ┌──────────────────┐ │
|
|
||||||
│ │ CommandType: 0 │ │
|
|
||||||
│ │ Parameter: "" │ ──────►│ 执行采样
|
|
||||||
│ └──────────────────┘ │
|
|
||||||
│ │
|
|
||||||
│ RemoteDebugCommand │
|
|
||||||
│ ┌──────────────────┐ │
|
|
||||||
│ │ CommandType: 1 │ │
|
|
||||||
│ │ Parameter: "open"│ ──────►│ 开启持续采样
|
|
||||||
│ └──────────────────┘ │
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 类关系图
|
|
||||||
|
|
||||||
```
|
|
||||||
DiagnosticReport (诊断报告)
|
|
||||||
│
|
|
||||||
└── List<DiagnosticPackageData> (包裹数据)
|
|
||||||
│
|
|
||||||
├── List<DiagnosticProviderInfo> (资源加载信息)
|
|
||||||
├── List<DiagnosticBundleInfo> (资源包信息)
|
|
||||||
└── List<DiagnosticOperationInfo> (异步操作信息)
|
|
||||||
│
|
|
||||||
└── List<DiagnosticOperationInfo> (子任务)
|
|
||||||
|
|
||||||
RemoteDebugBehaviour (运行时组件)
|
|
||||||
│
|
|
||||||
├── 接收 ──► RemoteDebugCommand
|
|
||||||
│
|
|
||||||
└── 发送 ──► DiagnosticReport
|
|
||||||
|
|
||||||
MockEditorConnection ◄────► MockPlayerConnection (Editor 模拟通信)
|
|
||||||
EditorConnection ◄────► PlayerConnection (真机通信)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **采样性能**:持续采样模式会每帧采集数据,可能影响性能,建议仅在调试时使用
|
|
||||||
2. **数据序列化**:使用 Unity 的 `JsonUtility` 进行序列化,受其限制(如不支持 Dictionary)
|
|
||||||
3. **版本兼容**:`DebuggerVersion` 用于版本控制,Editor 和 Player 版本不匹配时可能无法正常工作
|
|
||||||
4. **子任务深度**:`DiagnosticOperationInfo.Children` 存在序列化深度限制(10层)
|
|
||||||
5. **Editor 模式**:在 Editor 模式下使用 Mock 连接进行本地通信,无需真机
|
|
||||||
6. **真机调试**:真机调试需要通过 USB 连接,使用 Unity 的 `PlayerConnection` API
|
|
||||||
7. **线程安全**:所有操作应在主线程进行
|
|
||||||
8. **资源释放**:`MockEditorConnection` 和 `MockPlayerConnection` 在 Domain Reload 时会自动重置
|
|
||||||
@@ -1,607 +0,0 @@
|
|||||||
# DownloadSystem 下载模块
|
|
||||||
|
|
||||||
## 模块概述
|
|
||||||
|
|
||||||
DownloadSystem 是 YooAsset 资源管理系统的**底层网络下载层**,负责处理所有 HTTP 网络请求。该模块提供了统一的下载接口抽象,支持文件下载、断点续传、并发请求(由上层调度)、看门狗监控等功能。
|
|
||||||
|
|
||||||
### 可见性说明
|
|
||||||
|
|
||||||
DownloadSystem 属于 YooAsset Runtime 的内部基础模块,目录内大多数类型为 `internal`(仅供 YooAsset Runtime 内部程序集使用)。本文示例以"模块内部调用方式"展示;业务层建议优先通过 `ResourcePackage / FileSystem / ResourceManager` 等上层接口使用下载能力,避免直接依赖本模块的内部类型。
|
|
||||||
|
|
||||||
### 核心职责
|
|
||||||
|
|
||||||
- HTTP/HTTPS 文件下载
|
|
||||||
- 断点续传支持
|
|
||||||
- 看门狗超时保护
|
|
||||||
- 多种下载类型(文件/字节/文本/AssetBundle)
|
|
||||||
- 可插拔的网络库后端
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 边界与上层协作
|
|
||||||
|
|
||||||
DownloadSystem 的职责是提供"可替换后端 + 统一请求接口 + 轮询式生命周期"的基础能力:
|
|
||||||
|
|
||||||
- **本模块不负责并发队列/限流调度**:并发通常由上层同时创建多个 request 并自行控制并发数。
|
|
||||||
- **本模块不负责重试/回退策略**:失败后的重试、切换 CDN、降级等策略通常由上层系统实现。
|
|
||||||
- **本模块不负责持久化下载任务**:断点续传依赖本地已有文件与 `Range` 请求头,并由上层管理断点信息。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 设计目标
|
|
||||||
|
|
||||||
| 目标 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **可扩展性** | 支持可插拔的网络库后端(UnityWebRequest/BestHTTP/自研) |
|
|
||||||
| **鲁棒性** | 看门狗超时保护、自动清理失败文件、完整错误信息 |
|
|
||||||
| **高性能** | 轮询模式无阻塞、支持并发请求(并发数由上层调度) |
|
|
||||||
| **易用性** | 流畅的参数构建 API、清晰的状态转换 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 架构概念
|
|
||||||
|
|
||||||
### 分层架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────┐
|
|
||||||
│ 上层调用者 │
|
|
||||||
│ (FileSystem / ResourceManager) │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ IDownloadBackend │
|
|
||||||
│ (后端接口) │
|
|
||||||
│ 定义网络库合约,工厂模式创建请求 │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ IDownloadRequest │
|
|
||||||
│ (请求接口) │
|
|
||||||
│ 轮询式生命周期管理,状态机驱动 │
|
|
||||||
└─────────────────────────┬───────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌─────────────────────────▼───────────────────────────────┐
|
|
||||||
│ UnityWebRequest / 其他网络库 │
|
|
||||||
│ (底层实现) │
|
|
||||||
└─────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 核心组件
|
|
||||||
|
|
||||||
- **后端层 (IDownloadBackend)**: 定义网络库实现合约,通过工厂方法创建各类请求
|
|
||||||
- **请求层 (IDownloadRequest)**: 统一的请求生命周期管理,支持轮询驱动
|
|
||||||
- **参数层 (Args 结构体)**: 配置下载行为(超时、断点续传、看门狗等)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 文件结构
|
|
||||||
|
|
||||||
```
|
|
||||||
DownloadSystem/
|
|
||||||
├── Interface/ # 接口定义
|
|
||||||
│ ├── IDownloadBackend.cs # 后端接口(工厂模式)
|
|
||||||
│ └── IDownloadRequest.cs # 请求接口层次结构
|
|
||||||
│
|
|
||||||
├── UnityWebBackend/ # 默认后端实现
|
|
||||||
│ ├── UnityWebRequestBackend.cs # UnityWebRequest 后端
|
|
||||||
│ └── UnityWebRequestCreator.cs # UnityWebRequest 创建委托
|
|
||||||
│
|
|
||||||
├── UnityWebRequest/ # 默认请求实现
|
|
||||||
│ ├── UnityWebRequestBase.cs # 基础下载器(抽象类)
|
|
||||||
│ ├── UnityWebRequestFile.cs # 文件下载器
|
|
||||||
│ ├── UnityWebRequestHead.cs # HEAD 请求器
|
|
||||||
│ ├── UnityWebRequestBytes.cs # 字节下载器
|
|
||||||
│ ├── UnityWebRequestText.cs # 文本下载器
|
|
||||||
│ ├── UnityWebRequestAssetBundle.cs # AssetBundle 下载器
|
|
||||||
│ └── SimulateRequestFile.cs # 模拟下载器(编辑器用)
|
|
||||||
│
|
|
||||||
├── EDownloadRequestStatus.cs # 下载请求状态枚举
|
|
||||||
├── DownloadRequestArgs.cs # 请求参数结构体定义
|
|
||||||
├── DownloadSystemTools.cs # 工具函数
|
|
||||||
└── DownloadFailureCounter.cs # 请求失败计数器
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 接口说明
|
|
||||||
|
|
||||||
### IDownloadBackend(后端接口)
|
|
||||||
|
|
||||||
定义网络库实现的合约,通过工厂方法创建各类下载请求。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal interface IDownloadBackend : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 后端标识名称(用于日志/调试)
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定期驱动更新(部分第三方库需要)
|
|
||||||
/// </summary>
|
|
||||||
void Update();
|
|
||||||
|
|
||||||
// 工厂方法 - 创建各类请求
|
|
||||||
IDownloadHeadRequest CreateHeadRequest(DownloadDataRequestArgs args);
|
|
||||||
IDownloadFileRequest CreateFileRequest(DownloadFileRequestArgs args);
|
|
||||||
IDownloadBytesRequest CreateBytesRequest(DownloadDataRequestArgs args);
|
|
||||||
IDownloadTextRequest CreateTextRequest(DownloadDataRequestArgs args);
|
|
||||||
IDownloadAssetBundleRequest CreateAssetBundleRequest(DownloadAssetBundleRequestArgs args);
|
|
||||||
IDownloadFileRequest CreateSimulateRequest(DownloadSimulateRequestArgs args);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### IDownloadRequest(基础请求接口)
|
|
||||||
|
|
||||||
所有下载请求的通用接口,定义生命周期和状态管理。
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal interface IDownloadRequest : IDisposable
|
|
||||||
{
|
|
||||||
// 元信息
|
|
||||||
string URL { get; }
|
|
||||||
|
|
||||||
// 生命周期
|
|
||||||
bool IsDone { get; } // 访问时自动调用 PollingRequest()
|
|
||||||
EDownloadRequestStatus Status { get; }
|
|
||||||
|
|
||||||
// 进度跟踪
|
|
||||||
float DownloadProgress { get; } // 0f - 1f
|
|
||||||
long DownloadedBytes { get; } // 本次请求新增字节数
|
|
||||||
|
|
||||||
// 诊断信息
|
|
||||||
long HttpCode { get; }
|
|
||||||
string Error { get; }
|
|
||||||
|
|
||||||
// 生命周期方法
|
|
||||||
void SendRequest(); // 发起请求
|
|
||||||
void PollingRequest(); // 轮询状态
|
|
||||||
void AbortRequest(); // 中止请求
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 专化请求接口
|
|
||||||
|
|
||||||
| 接口 | 用途 | 特有能力 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `IDownloadHeadRequest` | HEAD 请求,获取响应头 | `ETag`, `LastModified`, `ContentLength`, `ContentType`, `GetResponseHeader(name)` |
|
|
||||||
| `IDownloadFileRequest` | 文件下载到本地 | `SavePath` |
|
|
||||||
| `IDownloadBytesRequest` | 下载到内存(字节数组) | `byte[] Result` |
|
|
||||||
| `IDownloadTextRequest` | 下载文本内容 | `string Result` |
|
|
||||||
| `IDownloadAssetBundleRequest` | 下载并加载 AssetBundle | `AssetBundle Result` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 结构体定义
|
|
||||||
|
|
||||||
### 请求状态枚举
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal enum EDownloadRequestStatus
|
|
||||||
{
|
|
||||||
None, // 未开始
|
|
||||||
Running, // 进行中
|
|
||||||
Succeed, // 已成功
|
|
||||||
Failed, // 已失败
|
|
||||||
Aborted // 已中止(用户中止或看门狗超时)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 请求参数结构体
|
|
||||||
|
|
||||||
#### DownloadFileRequestArgs(文件下载参数)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal struct DownloadFileRequestArgs
|
|
||||||
{
|
|
||||||
public readonly string URL; // 请求地址
|
|
||||||
public readonly int Timeout; // 响应超时(秒),0=不应用超时
|
|
||||||
public readonly int WatchdogTimeout; // 看门狗超时(秒),0=禁用
|
|
||||||
|
|
||||||
public readonly string SavePath; // 文件保存路径
|
|
||||||
public readonly bool AppendToFile; // 追加写入(断点续传)
|
|
||||||
public readonly bool RemoveFileOnAbort; // 中止时删除文件
|
|
||||||
public readonly long ResumeOffset; // 断点续传起始位置(>0 时推荐由后端设置 Range 头)
|
|
||||||
|
|
||||||
public Dictionary<string, string> Headers; // 自定义请求头(可选)
|
|
||||||
|
|
||||||
public DownloadFileRequestArgs(
|
|
||||||
string url,
|
|
||||||
string savePath,
|
|
||||||
int timeout,
|
|
||||||
int watchdogTimeout,
|
|
||||||
bool appendToFile = false,
|
|
||||||
bool removeFileOnAbort = true,
|
|
||||||
long resumeOffset = 0);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 添加请求头(注意:相同 key 重复添加会抛异常)
|
|
||||||
/// </summary>
|
|
||||||
public void AddRequestHeader(string name, string value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DownloadDataRequestArgs(数据下载参数)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal struct DownloadDataRequestArgs
|
|
||||||
{
|
|
||||||
public readonly string URL; // 请求地址
|
|
||||||
public readonly int Timeout; // 响应超时(秒),0=不应用超时
|
|
||||||
public readonly int WatchdogTimeout; // 看门狗超时(秒),0=禁用
|
|
||||||
public Dictionary<string, string> Headers; // 自定义请求头(可选)
|
|
||||||
|
|
||||||
public DownloadDataRequestArgs(string url, int timeout, int watchdogTimeout);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 添加请求头(注意:相同 key 重复添加会抛异常)
|
|
||||||
/// </summary>
|
|
||||||
public void AddRequestHeader(string name, string value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DownloadAssetBundleRequestArgs(AssetBundle 下载参数)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal struct DownloadAssetBundleRequestArgs
|
|
||||||
{
|
|
||||||
public readonly string URL; // 请求地址
|
|
||||||
public readonly int Timeout; // 响应超时(秒),0=不应用超时
|
|
||||||
public readonly int WatchdogTimeout; // 看门狗超时(秒),0=禁用
|
|
||||||
|
|
||||||
public readonly bool DisableUnityWebCache; // 禁用 Unity 缓存(默认 true)
|
|
||||||
public readonly string FileHash; // 文件哈希(缓存启用时需要,且不能为空)
|
|
||||||
public readonly uint UnityCRC; // Unity CRC 校验值
|
|
||||||
|
|
||||||
public Dictionary<string, string> Headers; // 自定义请求头(可选)
|
|
||||||
|
|
||||||
public DownloadAssetBundleRequestArgs(
|
|
||||||
string url,
|
|
||||||
int timeout,
|
|
||||||
int watchdogTimeout,
|
|
||||||
bool disableUnityWebCache = true,
|
|
||||||
string fileHash = null,
|
|
||||||
uint unityCrc = 0);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 添加请求头(注意:相同 key 重复添加会抛异常)
|
|
||||||
/// </summary>
|
|
||||||
public void AddRequestHeader(string name, string value);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### DownloadSimulateRequestArgs(模拟下载参数)
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
internal struct DownloadSimulateRequestArgs
|
|
||||||
{
|
|
||||||
public readonly string URL; // 标识符
|
|
||||||
public readonly long FileSize; // 模拟文件大小
|
|
||||||
public readonly long DownloadSpeed; // 模拟速度(字节/秒),默认 1MB/s
|
|
||||||
|
|
||||||
public DownloadSimulateRequestArgs(string url, long fileSize, long downloadSpeed = 1024 * 1024);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心类说明
|
|
||||||
|
|
||||||
### UnityWebRequestBackend
|
|
||||||
|
|
||||||
默认的后端实现,基于 Unity 的 UnityWebRequest API。
|
|
||||||
|
|
||||||
**特性:**
|
|
||||||
- 支持自定义 UnityWebRequest 创建方式(证书验证、代理等)
|
|
||||||
- 无需手动调用 Update(),UnityWebRequest 自动驱动
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// 自定义 UnityWebRequest 创建(建议通过 backend 构造函数传入)
|
|
||||||
UnityWebRequestCreator creator = (url, method) =>
|
|
||||||
{
|
|
||||||
var request = new UnityWebRequest(url, method);
|
|
||||||
// 自定义配置...
|
|
||||||
return request;
|
|
||||||
};
|
|
||||||
|
|
||||||
IDownloadBackend backend = new UnityWebRequestBackend(creator);
|
|
||||||
```
|
|
||||||
|
|
||||||
### UnityWebRequestBase
|
|
||||||
|
|
||||||
抽象基类,封装所有下载器的通用逻辑。
|
|
||||||
|
|
||||||
**职责:**
|
|
||||||
- 管理请求生命周期和状态转换
|
|
||||||
- 实现看门狗监控机制
|
|
||||||
- 追踪下载进度和字节数
|
|
||||||
- 处理超时和错误
|
|
||||||
|
|
||||||
**生命周期:**
|
|
||||||
|
|
||||||
```
|
|
||||||
None ──► SendRequest() ──► Running ──► PollingRequest() ──┬──► Succeed
|
|
||||||
├──► Failed
|
|
||||||
└──► Aborted
|
|
||||||
```
|
|
||||||
|
|
||||||
### 具体下载器
|
|
||||||
|
|
||||||
| 下载器 | 实现接口 | 使用场景 |
|
|
||||||
|--------|----------|----------|
|
|
||||||
| `UnityWebRequestFile` | `IDownloadFileRequest` | 大文件下载到本地 |
|
|
||||||
| `UnityWebRequestHead` | `IDownloadHeadRequest` | 检查资源信息 |
|
|
||||||
| `UnityWebRequestBytes` | `IDownloadBytesRequest` | 小文件内存加载 |
|
|
||||||
| `UnityWebRequestText` | `IDownloadTextRequest` | 文本文件下载 |
|
|
||||||
| `UnityWebRequestAssetBundle` | `IDownloadAssetBundleRequest` | AB 包下载加载 |
|
|
||||||
| `SimulateRequestFile` | `IDownloadFileRequest` | 编辑器模拟下载 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 使用示例
|
|
||||||
|
|
||||||
### 基础文件下载
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
IDownloadBackend backend = new UnityWebRequestBackend();
|
|
||||||
IDownloadFileRequest request = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 1. 创建请求
|
|
||||||
var args = new DownloadFileRequestArgs(
|
|
||||||
url: "https://example.com/file.zip",
|
|
||||||
savePath: "/path/to/save/file.zip",
|
|
||||||
timeout: 30,
|
|
||||||
watchdogTimeout: 0);
|
|
||||||
request = backend.CreateFileRequest(args);
|
|
||||||
|
|
||||||
// 2. 发起并轮询
|
|
||||||
request.SendRequest();
|
|
||||||
while (!request.IsDone)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
float progress = request.DownloadProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 检查结果
|
|
||||||
if (request.Status == EDownloadRequestStatus.Succeed)
|
|
||||||
Debug.Log("下载成功");
|
|
||||||
else
|
|
||||||
Debug.LogError($"下载失败: {request.Error}");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// 4. 清理资源
|
|
||||||
request?.Dispose();
|
|
||||||
backend.Dispose();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 断点续传
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// 获取已下载的文件大小
|
|
||||||
long existingFileSize = new FileInfo(savePath).Length;
|
|
||||||
|
|
||||||
var args = new DownloadFileRequestArgs(
|
|
||||||
url: url,
|
|
||||||
savePath: savePath,
|
|
||||||
timeout: 30,
|
|
||||||
watchdogTimeout: 0,
|
|
||||||
appendToFile: true, // 追加写入
|
|
||||||
removeFileOnAbort: false, // 中止时保留文件
|
|
||||||
resumeOffset: existingFileSize); // 断点位置
|
|
||||||
|
|
||||||
IDownloadFileRequest request = backend.CreateFileRequest(args);
|
|
||||||
request.SendRequest();
|
|
||||||
// ... 轮询等待完成
|
|
||||||
```
|
|
||||||
|
|
||||||
### 看门狗保护
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var args = new DownloadFileRequestArgs(
|
|
||||||
url: url,
|
|
||||||
savePath: path,
|
|
||||||
timeout: 30,
|
|
||||||
watchdogTimeout: 30); // 30秒无数据自动中止
|
|
||||||
|
|
||||||
IDownloadFileRequest request = backend.CreateFileRequest(args);
|
|
||||||
request.SendRequest();
|
|
||||||
|
|
||||||
while (!request.IsDone)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否因看门狗超时而中止
|
|
||||||
if (request.Status == EDownloadRequestStatus.Aborted)
|
|
||||||
{
|
|
||||||
Debug.LogWarning("下载超时,已自动中止");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### HEAD 请求获取文件信息
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var args = new DownloadDataRequestArgs(
|
|
||||||
url: "https://example.com/file.zip",
|
|
||||||
timeout: 30,
|
|
||||||
watchdogTimeout: 0);
|
|
||||||
|
|
||||||
IDownloadHeadRequest request = backend.CreateHeadRequest(args);
|
|
||||||
request.SendRequest();
|
|
||||||
|
|
||||||
while (!request.IsDone)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Status == EDownloadRequestStatus.Succeed)
|
|
||||||
{
|
|
||||||
long fileSize = request.ContentLength;
|
|
||||||
string etag = request.ETag;
|
|
||||||
string lastModified = request.LastModified;
|
|
||||||
|
|
||||||
Debug.Log($"文件大小: {fileSize}, ETag: {etag}");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 下载字节数据
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
var args = new DownloadDataRequestArgs(
|
|
||||||
url: "https://example.com/data.json",
|
|
||||||
timeout: 30,
|
|
||||||
watchdogTimeout: 0);
|
|
||||||
|
|
||||||
IDownloadBytesRequest request = backend.CreateBytesRequest(args);
|
|
||||||
request.SendRequest();
|
|
||||||
|
|
||||||
while (!request.IsDone)
|
|
||||||
{
|
|
||||||
await Task.Yield();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request.Status == EDownloadRequestStatus.Succeed)
|
|
||||||
{
|
|
||||||
byte[] data = request.Result;
|
|
||||||
// 处理数据...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 设计模式
|
|
||||||
|
|
||||||
### 工厂模式
|
|
||||||
|
|
||||||
`IDownloadBackend` 作为工厂接口,创建各类下载请求对象:
|
|
||||||
|
|
||||||
```
|
|
||||||
IDownloadBackend
|
|
||||||
├── CreateHeadRequest() ──► IDownloadHeadRequest
|
|
||||||
├── CreateFileRequest() ──► IDownloadFileRequest
|
|
||||||
├── CreateBytesRequest() ──► IDownloadBytesRequest
|
|
||||||
├── CreateTextRequest() ──► IDownloadTextRequest
|
|
||||||
├── CreateAssetBundleRequest() ──► IDownloadAssetBundleRequest
|
|
||||||
└── CreateSimulateRequest() ──► IDownloadFileRequest
|
|
||||||
```
|
|
||||||
|
|
||||||
### 策略模式
|
|
||||||
|
|
||||||
通过实现 `IDownloadBackend` 接口,可以替换底层网络库:
|
|
||||||
|
|
||||||
```
|
|
||||||
IDownloadBackend (接口)
|
|
||||||
├── UnityWebRequestBackend (默认实现)
|
|
||||||
├── BestHTTPBackend (可扩展)
|
|
||||||
└── CustomBackend (自定义)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 状态机模式
|
|
||||||
|
|
||||||
请求生命周期通过状态机管理:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────┐ SendRequest() ┌─────────┐
|
|
||||||
│ None │ ──────────────────► │ Running │
|
|
||||||
└──────┘ └────┬────┘
|
|
||||||
│ PollingRequest()
|
|
||||||
┌─────────────┼─────────────┐
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────┐ ┌──────────┐ ┌─────────┐
|
|
||||||
│ Succeed │ │ Failed │ │ Aborted │
|
|
||||||
└─────────┘ └──────────┘ └─────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 看门狗模式
|
|
||||||
|
|
||||||
监控数据接收,防止网络卡顿导致请求无限等待:
|
|
||||||
|
|
||||||
```
|
|
||||||
每帧轮询 PollingRequest()
|
|
||||||
│
|
|
||||||
├── 收到新数据 ──► 重置计时器
|
|
||||||
│
|
|
||||||
└── 未收到数据 ──► 计时器累加
|
|
||||||
│
|
|
||||||
└── 超过 WatchdogTimeout ──► AbortRequest()
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 类继承关系
|
|
||||||
|
|
||||||
```
|
|
||||||
IDownloadRequest (基础接口)
|
|
||||||
│
|
|
||||||
├── IDownloadHeadRequest (HEAD 请求)
|
|
||||||
├── IDownloadFileRequest (文件下载)
|
|
||||||
├── IDownloadBytesRequest (字节下载)
|
|
||||||
├── IDownloadTextRequest (文本下载)
|
|
||||||
└── IDownloadAssetBundleRequest (AssetBundle 下载)
|
|
||||||
|
|
||||||
UnityWebRequestBase (抽象基类)
|
|
||||||
│
|
|
||||||
├── UnityWebRequestFile ──► IDownloadFileRequest
|
|
||||||
├── UnityWebRequestHead ──► IDownloadHeadRequest
|
|
||||||
├── UnityWebRequestBytes ──► IDownloadBytesRequest
|
|
||||||
├── UnityWebRequestText ──► IDownloadTextRequest
|
|
||||||
└── UnityWebRequestAssetBundle ──► IDownloadAssetBundleRequest
|
|
||||||
|
|
||||||
SimulateRequestFile (独立实现) ──► IDownloadFileRequest
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 工具类
|
|
||||||
|
|
||||||
### DownloadSystemTools
|
|
||||||
|
|
||||||
提供跨平台的 URL 转换和判断功能:
|
|
||||||
|
|
||||||
| 方法 | 参数 | 返回值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `ToLocalURL(path)` | `string path` 本地文件路径 | 可用于 UnityWebRequest 的文件协议 URL | 转换本地路径为文件协议 URL(自动处理特殊字符) |
|
|
||||||
| `IsLocalFileURL(url)` | `string url` 要判断的 URL | `bool` 是否为本地文件 URL | 判断 URL 是否为 `file:` 或 `jar:file:` 协议 |
|
|
||||||
|
|
||||||
### DownloadFailureCounter
|
|
||||||
|
|
||||||
网络请求失败计数器(诊断用):
|
|
||||||
|
|
||||||
- **线程安全**:内部使用 `Dictionary` 且未加锁,约定只在 Unity 主线程调用;如需在多线程/回调线程调用,请在外层加锁或改为并发容器实现
|
|
||||||
- **Key 格式**:`$"{packageName}_{eventName}"`
|
|
||||||
- **统计口径**:**仅统计网络请求失败**(`IDownloadRequest.Status != Succeed` 时记录),不统计内容为空、校验失败、解析失败等业务层失败
|
|
||||||
|
|
||||||
| 方法 | 参数 | 返回值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `RecordFailure(packageName, eventName)` | `string packageName` 资源包名称, `string eventName` 事件名称 | `void` | 记录一次失败 |
|
|
||||||
| `GetFailureCount(packageName, eventName)` | `string packageName` 资源包名称, `string eventName` 事件名称 | `int` 失败次数(未记录过返回 0) | 获取失败次数 |
|
|
||||||
|
|
||||||
```csharp
|
|
||||||
// 记录失败
|
|
||||||
DownloadFailureCounter.RecordFailure(packageName, eventName);
|
|
||||||
|
|
||||||
// 查询失败次数
|
|
||||||
int count = DownloadFailureCounter.GetFailureCount(packageName, eventName);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. **资源释放**:使用完毕后务必调用 `Dispose()` 释放资源
|
|
||||||
- `AbortRequest()` 仅用于中止请求与切换状态,不等同于释放资源;无论成功/失败/中止都需要 `Dispose()`
|
|
||||||
- 第三方 `IDownloadBackend` 可能持有原生资源/线程/连接池等,上层在不再使用时也应调用 `backend.Dispose()`
|
|
||||||
- 推荐使用 `try/finally` 确保释放(尤其是上层可能提前中止的场景)
|
|
||||||
2. **断点续传**:需要服务器支持 `Range` 请求头和 `206 Partial Content` 响应
|
|
||||||
- 若服务端不支持 Range 仍返回 200,全量内容可能会被追加写入,导致文件损坏
|
|
||||||
3. **看门狗超时**:设置为 0 表示禁用,建议根据网络环境设置合理值
|
|
||||||
4. **内存下载**:`IDownloadBytesRequest` 会将整个响应体加载到内存,不适合大文件
|
|
||||||
5. **驱动更新**:部分第三方网络库实现的 backend 可能需要每帧调用 `IDownloadBackend.Update()` 进行驱动
|
|
||||||
6. **中止语义**:`Aborted` 可能来自用户主动 `AbortRequest()` 或看门狗超时;中止场景下 `HttpCode/Error` 可能为默认值(例如 0/空)
|
|
||||||
7. **线程安全**:所有下载请求的创建和轮询应在主线程进行
|
|
||||||
8. **模拟下载器**:`SimulateRequestFile` 仅用于模拟进度,不会落盘,且 `SavePath` 始终为 `null`;成功时 `HttpCode` 固定为 `200`
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 5fa2b66c20800124c8dd5cb77e854ce3
|
guid: d348d244f16bb9a4c9537f045524af44
|
||||||
TextScriptImporter:
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 2afe3d5ffb611b241919112b40d9c7d0
|
guid: 7c9bb74a55a8cbc4788da87c55b3a541
|
||||||
TextScriptImporter:
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
userData:
|
userData:
|
||||||
assetBundleName:
|
assetBundleName:
|
||||||
@@ -17,7 +17,7 @@ namespace YooAsset
|
|||||||
Middle = 2,
|
Middle = 2,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 验证文件大小和CRC
|
/// 验证文件CRC
|
||||||
/// </summary>
|
/// </summary>
|
||||||
High = 3,
|
High = 3,
|
||||||
}
|
}
|
||||||
@@ -8,22 +8,25 @@ namespace YooAsset
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 文件校验
|
/// 文件校验
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static EFileVerifyResult FileVerify(string filePath, long fileSize, uint fileCRC, EFileVerifyLevel verifyLevel)
|
public static EFileVerifyResult FileVerify(string filePath, long fileSize, uint fileCRC)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (File.Exists(filePath) == false)
|
if (File.Exists(filePath) == false)
|
||||||
return EFileVerifyResult.DataFileNotExisted;
|
return EFileVerifyResult.DataFileNotExisted;
|
||||||
|
|
||||||
// 先验证文件大小
|
// 验证文件大小
|
||||||
long size = FileUtility.GetFileSize(filePath);
|
if (fileSize > 0)
|
||||||
if (size < fileSize)
|
{
|
||||||
return EFileVerifyResult.FileNotComplete;
|
long size = FileUtility.GetFileSize(filePath);
|
||||||
else if (size > fileSize)
|
if (size < fileSize)
|
||||||
return EFileVerifyResult.FileOverflow;
|
return EFileVerifyResult.FileNotComplete;
|
||||||
|
else if (size > fileSize)
|
||||||
|
return EFileVerifyResult.FileOverflow;
|
||||||
|
}
|
||||||
|
|
||||||
// 再验证文件CRC
|
// 验证文件CRC
|
||||||
if (verifyLevel == EFileVerifyLevel.High)
|
if (fileCRC > 0)
|
||||||
{
|
{
|
||||||
uint crc = HashUtility.FileCRC32Value(filePath);
|
uint crc = HashUtility.FileCRC32Value(filePath);
|
||||||
if (crc == fileCRC)
|
if (crc == fileCRC)
|
||||||
@@ -36,8 +39,9 @@ namespace YooAsset
|
|||||||
return EFileVerifyResult.Succeed;
|
return EFileVerifyResult.Succeed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
YooLogger.Error($"File verify exception : {ex.Message}");
|
||||||
return EFileVerifyResult.Exception;
|
return EFileVerifyResult.Exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
Assets/YooAsset/Runtime/FileCache/Interfaces.meta
Normal file
8
Assets/YooAsset/Runtime/FileCache/Interfaces.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: eb3085b0049518a4db1d817e1486c879
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
14
Assets/YooAsset/Runtime/FileCache/Interfaces/ICacheEntry.cs
Normal file
14
Assets/YooAsset/Runtime/FileCache/Interfaces/ICacheEntry.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存记录接口
|
||||||
|
/// </summary>
|
||||||
|
internal interface ICacheEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Bundle唯一标识
|
||||||
|
/// </summary>
|
||||||
|
string BundleGUID { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: ae2176c4ea6fbb3478bf8757def34cd7
|
guid: 0f4a2df973e802b489c3df5917c45714
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
67
Assets/YooAsset/Runtime/FileCache/Interfaces/IFileCache.cs
Normal file
67
Assets/YooAsset/Runtime/FileCache/Interfaces/IFileCache.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 文件缓存系统接口
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntry">缓存记录类型</typeparam>
|
||||||
|
internal interface IFileCache<TEntry> where TEntry : ICacheEntry
|
||||||
|
{
|
||||||
|
#region 状态属性
|
||||||
|
/// <summary>
|
||||||
|
/// 包裹名称
|
||||||
|
/// </summary>
|
||||||
|
string PackageName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存根目录
|
||||||
|
/// </summary>
|
||||||
|
string RootPath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 只读属性
|
||||||
|
/// </summary>
|
||||||
|
bool IsReadOnly { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已占用空间(字节)
|
||||||
|
/// </summary>
|
||||||
|
long SpaceOccupied { get; }
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 异步操作
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化缓存
|
||||||
|
/// </summary>
|
||||||
|
FCInitializeOperation InitializeAsync(FCInitializeOptions options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 存储缓存文件
|
||||||
|
/// </summary>
|
||||||
|
FCStoreCacheOperation StoreCacheAsync(FCStoreCacheOptions options);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理缓存文件
|
||||||
|
/// </summary>
|
||||||
|
FCClearCacheOperation ClearCacheAsync(FCClearCacheOptions options);
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 查询方法
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已缓存指定 Bundle
|
||||||
|
/// </summary>
|
||||||
|
bool IsCached(string bundleGUID);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取缓存记录
|
||||||
|
/// </summary>
|
||||||
|
TEntry GetEntry(string bundleGUID);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有的缓存记录
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyCollection<TEntry> GetAllEntries();
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 273f6d8fdd5b38e49be55c5c38daaf66
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/YooAsset/Runtime/FileCache/Operations.meta
Normal file
8
Assets/YooAsset/Runtime/FileCache/Operations.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b80693487b8ebcb49b52686fe907cbf8
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal class FCClearCacheOperation : AsyncOperationBase
|
||||||
|
{
|
||||||
|
private enum ESteps
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
CheckOptions,
|
||||||
|
ClearCache,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
private BundleCache _cache;
|
||||||
|
private FCClearCacheOptions _options;
|
||||||
|
private int _clearFileTotalCount;
|
||||||
|
private List<string> _bundleGUIDs;
|
||||||
|
private ESteps _steps = ESteps.None;
|
||||||
|
|
||||||
|
public FCClearCacheOperation(BundleCache cache, FCClearCacheOptions options)
|
||||||
|
{
|
||||||
|
_cache = cache;
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
internal override void InternalStart()
|
||||||
|
{
|
||||||
|
_steps = ESteps.CheckOptions;
|
||||||
|
}
|
||||||
|
internal override void InternalUpdate()
|
||||||
|
{
|
||||||
|
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_steps == ESteps.CheckOptions)
|
||||||
|
{
|
||||||
|
if (_options.BundleGUIDs == null || _options.BundleGUIDs.Count == 0)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bundleGUIDs = _options.BundleGUIDs.ToList();
|
||||||
|
_clearFileTotalCount = _options.BundleGUIDs.Count;
|
||||||
|
_steps = ESteps.ClearCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_steps == ESteps.ClearCache)
|
||||||
|
{
|
||||||
|
for (int i = _bundleGUIDs.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
string bundleGUID = _bundleGUIDs[i];
|
||||||
|
_cache.RemoveEntry(bundleGUID);
|
||||||
|
_bundleGUIDs.RemoveAt(i);
|
||||||
|
if (IsBusy)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_clearFileTotalCount == 0)
|
||||||
|
Progress = 1.0f;
|
||||||
|
else
|
||||||
|
Progress = 1.0f - ((float)_bundleGUIDs.Count / _clearFileTotalCount);
|
||||||
|
|
||||||
|
if (_bundleGUIDs.Count == 0)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal override void InternalWaitForCompletion()
|
||||||
|
{
|
||||||
|
ExecuteBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fb3347395a55d3048bcec202aca0e4a7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal struct FCClearCacheOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 清理指定缓存列表
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string> BundleGUIDs { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 03f8c5e4c7af7df4387f56d045d8d357
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal class FCInitializeOperation : AsyncOperationBase
|
||||||
|
{
|
||||||
|
private enum ESteps
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
SearchCacheFiles,
|
||||||
|
VerifyCacheFiles,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly BundleCache _cache;
|
||||||
|
private readonly FCInitializeOptions _options;
|
||||||
|
private SearchCacheFilesOperation _searchCacheFilesOp;
|
||||||
|
private VerifyCacheFilesOperation _verifyCacheFilesOp;
|
||||||
|
private ESteps _steps = ESteps.None;
|
||||||
|
|
||||||
|
public FCInitializeOperation(BundleCache cache, FCInitializeOptions options)
|
||||||
|
{
|
||||||
|
_cache = cache;
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
internal override void InternalStart()
|
||||||
|
{
|
||||||
|
_steps = ESteps.SearchCacheFiles;
|
||||||
|
}
|
||||||
|
internal override void InternalUpdate()
|
||||||
|
{
|
||||||
|
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_steps == ESteps.SearchCacheFiles)
|
||||||
|
{
|
||||||
|
if (_searchCacheFilesOp == null)
|
||||||
|
{
|
||||||
|
_searchCacheFilesOp = new SearchCacheFilesOperation(_cache);
|
||||||
|
_searchCacheFilesOp.StartOperation();
|
||||||
|
AddChildOperation(_searchCacheFilesOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
_searchCacheFilesOp.UpdateOperation();
|
||||||
|
Progress = _searchCacheFilesOp.Progress;
|
||||||
|
if (_searchCacheFilesOp.IsDone == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_steps = ESteps.VerifyCacheFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_steps == ESteps.VerifyCacheFiles)
|
||||||
|
{
|
||||||
|
if (_verifyCacheFilesOp == null)
|
||||||
|
{
|
||||||
|
_verifyCacheFilesOp = new VerifyCacheFilesOperation(_cache, _options.FileVerifyLevel, _options.FileVerifyMaxConcurrency, _searchCacheFilesOp.Result);
|
||||||
|
_verifyCacheFilesOp.StartOperation();
|
||||||
|
AddChildOperation(_verifyCacheFilesOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
_verifyCacheFilesOp.UpdateOperation();
|
||||||
|
Progress = _verifyCacheFilesOp.Progress;
|
||||||
|
if (_verifyCacheFilesOp.IsDone == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_verifyCacheFilesOp.Status == EOperationStatus.Succeeded)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Failed;
|
||||||
|
Error = _verifyCacheFilesOp.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9be990c57fe162845ba3893188a68f8b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal struct FCInitializeOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 文件校验最大并发数
|
||||||
|
/// </summary>
|
||||||
|
public int FileVerifyMaxConcurrency { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文件校验级别
|
||||||
|
/// </summary>
|
||||||
|
public EFileVerifyLevel FileVerifyLevel { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 75f6c6588c154e145b09fa36cc6f2602
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal class FCStoreCacheOperation : AsyncOperationBase
|
||||||
|
{
|
||||||
|
private enum ESteps
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Check,
|
||||||
|
VerifyFile,
|
||||||
|
CacheFile,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
private BundleCache _cache;
|
||||||
|
private readonly FCStoreCacheOptions _options;
|
||||||
|
private VerifyTempFileOperation _verifyOperation;
|
||||||
|
private ESteps _steps = ESteps.None;
|
||||||
|
|
||||||
|
public FCStoreCacheOperation(BundleCache cache, FCStoreCacheOptions options)
|
||||||
|
{
|
||||||
|
_cache = cache;
|
||||||
|
_options = options;
|
||||||
|
}
|
||||||
|
internal override void InternalStart()
|
||||||
|
{
|
||||||
|
_steps = ESteps.Check;
|
||||||
|
}
|
||||||
|
internal override void InternalUpdate()
|
||||||
|
{
|
||||||
|
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_steps == ESteps.Check)
|
||||||
|
{
|
||||||
|
if (_cache.IsReadOnly)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Failed;
|
||||||
|
Error = $"{nameof(BundleCache)} is readonly.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_cache.IsCached(_options.Bundle.BundleGUID))
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Failed;
|
||||||
|
Error = "The bundle is cached.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_steps = ESteps.VerifyFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_steps == ESteps.VerifyFile)
|
||||||
|
{
|
||||||
|
if (_verifyOperation == null)
|
||||||
|
{
|
||||||
|
var element = new TempFileInfo(_options.FilePath, _options.Bundle.FileCRC, _options.Bundle.FileSize);
|
||||||
|
_verifyOperation = new VerifyTempFileOperation(element);
|
||||||
|
_verifyOperation.StartOperation();
|
||||||
|
AddChildOperation(_verifyOperation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsWaitForCompletion)
|
||||||
|
_verifyOperation.WaitForCompletion();
|
||||||
|
|
||||||
|
_verifyOperation.UpdateOperation();
|
||||||
|
if (_verifyOperation.IsDone == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_verifyOperation.Status == EOperationStatus.Succeeded)
|
||||||
|
{
|
||||||
|
_steps = ESteps.CacheFile;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Failed;
|
||||||
|
Error = _verifyOperation.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_steps == ESteps.CacheFile)
|
||||||
|
{
|
||||||
|
string infoFilePath = _cache.GetInfoFilePath(_options.Bundle);
|
||||||
|
string dataFilePath = _cache.GetDataFilePath(_options.Bundle);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(infoFilePath))
|
||||||
|
File.Delete(infoFilePath);
|
||||||
|
if (File.Exists(dataFilePath))
|
||||||
|
File.Delete(dataFilePath);
|
||||||
|
|
||||||
|
// 拷贝数据文件
|
||||||
|
FileUtility.CreateFileDirectory(dataFilePath);
|
||||||
|
FileInfo fileInfo = new FileInfo(_options.FilePath);
|
||||||
|
fileInfo.CopyTo(dataFilePath, true);
|
||||||
|
|
||||||
|
// 写入信息文件
|
||||||
|
FileUtility.CreateFileDirectory(infoFilePath);
|
||||||
|
using (FileStream fs = new FileStream(infoFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
|
{
|
||||||
|
_cache.SharedBuffer.Clear();
|
||||||
|
_cache.SharedBuffer.WriteUInt32(_options.Bundle.FileCRC);
|
||||||
|
_cache.SharedBuffer.WriteInt64(_options.Bundle.FileSize);
|
||||||
|
_cache.SharedBuffer.WriteToStream(fs);
|
||||||
|
fs.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Failed;
|
||||||
|
Error = $"Failed to write cache file. Error: {ex.Message}";
|
||||||
|
YooLogger.Error(Error);
|
||||||
|
return; //失败后直接返回
|
||||||
|
}
|
||||||
|
|
||||||
|
var cacheEntry = new BundleCacheEntry(_options.Bundle.BundleGUID, infoFilePath, dataFilePath, _options.Bundle.FileCRC, _options.Bundle.FileSize);
|
||||||
|
_cache.AddEntry(_options.Bundle.BundleGUID, cacheEntry);
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal override void InternalWaitForCompletion()
|
||||||
|
{
|
||||||
|
ExecuteBatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 2f5c954b2aaf2524790dbfb2c5251da2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal struct FCStoreCacheOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 要缓存的资源包
|
||||||
|
/// </summary>
|
||||||
|
public PackageBundle Bundle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 要缓存的文件路径
|
||||||
|
/// </summary>
|
||||||
|
public string FilePath { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cbe6c6f462ef0c9429568f9d1df559f6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
8
Assets/YooAsset/Runtime/FileCache/Services.meta
Normal file
8
Assets/YooAsset/Runtime/FileCache/Services.meta
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 21f2bb59b8b32e24b96619bea464a48c
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: cd27cacfb758c3546a5015d236e2f539
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal class BundleCache : IFileCache<BundleCacheEntry>
|
||||||
|
{
|
||||||
|
private const int HashFolderLength = 2;
|
||||||
|
|
||||||
|
// 缓存索引
|
||||||
|
private readonly Dictionary<string, BundleCacheEntry> _caches = new Dictionary<string, BundleCacheEntry>(10000);
|
||||||
|
|
||||||
|
// 路径缓存
|
||||||
|
private readonly Dictionary<string, string> _dataFilePathMapping = new Dictionary<string, string>(10000);
|
||||||
|
private readonly Dictionary<string, string> _infoFilePathMapping = new Dictionary<string, string>(10000);
|
||||||
|
|
||||||
|
// 共享缓冲区
|
||||||
|
internal readonly BufferWriter SharedBuffer = new BufferWriter(1024);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 包裹名称
|
||||||
|
/// </summary>
|
||||||
|
public string PackageName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存根目录
|
||||||
|
/// </summary>
|
||||||
|
public string RootPath { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 追加文件扩展名
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool AppendFileExtension;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 只读属性
|
||||||
|
/// </summary>
|
||||||
|
public bool IsReadOnly { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存文件数量
|
||||||
|
/// </summary>
|
||||||
|
public int FileCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _caches.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 已占用空间
|
||||||
|
/// 说明:按缓存索引累计
|
||||||
|
/// </summary>
|
||||||
|
public long SpaceOccupied { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public BundleCache(string packageName, string rootPath, bool appendFileExtension)
|
||||||
|
{
|
||||||
|
PackageName = packageName;
|
||||||
|
RootPath = rootPath;
|
||||||
|
AppendFileExtension = appendFileExtension;
|
||||||
|
IsReadOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化缓存
|
||||||
|
/// </summary>
|
||||||
|
public FCInitializeOperation InitializeAsync(FCInitializeOptions options)
|
||||||
|
{
|
||||||
|
var operation = new FCInitializeOperation(this, options);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 存储缓存文件
|
||||||
|
/// </summary>
|
||||||
|
public FCStoreCacheOperation StoreCacheAsync(FCStoreCacheOptions options)
|
||||||
|
{
|
||||||
|
var operation = new FCStoreCacheOperation(this, options);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清理缓存文件
|
||||||
|
/// </summary>
|
||||||
|
public FCClearCacheOperation ClearCacheAsync(FCClearCacheOptions options)
|
||||||
|
{
|
||||||
|
var operation = new FCClearCacheOperation(this, options);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已缓存指定 Bundle
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCached(string bundleGUID)
|
||||||
|
{
|
||||||
|
return _caches.ContainsKey(bundleGUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取缓存记录
|
||||||
|
/// </summary>
|
||||||
|
public BundleCacheEntry GetEntry(string bundleGUID)
|
||||||
|
{
|
||||||
|
if (_caches.TryGetValue(bundleGUID, out BundleCacheEntry entry))
|
||||||
|
return entry;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有的缓存记录
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyCollection<BundleCacheEntry> GetAllEntries()
|
||||||
|
{
|
||||||
|
return _caches.Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 内部方法
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 Bundle 数据文件路径
|
||||||
|
/// </summary>
|
||||||
|
internal string GetDataFilePath(PackageBundle bundle)
|
||||||
|
{
|
||||||
|
if (_dataFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
|
||||||
|
{
|
||||||
|
string folderName = GetHashFolderName(bundle.FileHash);
|
||||||
|
filePath = PathUtility.Combine(RootPath, folderName, bundle.BundleGUID, BundleCacheDefine.BundleDataFileName);
|
||||||
|
if (AppendFileExtension)
|
||||||
|
filePath += bundle.FileExtension;
|
||||||
|
_dataFilePathMapping.Add(bundle.BundleGUID, filePath);
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 Bundle 信息文件路径
|
||||||
|
/// </summary>
|
||||||
|
internal string GetInfoFilePath(PackageBundle bundle)
|
||||||
|
{
|
||||||
|
if (_infoFilePathMapping.TryGetValue(bundle.BundleGUID, out string filePath) == false)
|
||||||
|
{
|
||||||
|
string folderName = GetHashFolderName(bundle.FileHash);
|
||||||
|
filePath = PathUtility.Combine(RootPath, folderName, bundle.BundleGUID, BundleCacheDefine.BundleInfoFileName);
|
||||||
|
_infoFilePathMapping.Add(bundle.BundleGUID, filePath);
|
||||||
|
}
|
||||||
|
return filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加指定缓存
|
||||||
|
/// </summary>
|
||||||
|
internal void AddEntry(string bundleGUID, BundleCacheEntry entry)
|
||||||
|
{
|
||||||
|
if (_caches.ContainsKey(bundleGUID))
|
||||||
|
throw new YooInternalException($"Cache already existed: {bundleGUID}");
|
||||||
|
|
||||||
|
_caches.Add(bundleGUID, entry);
|
||||||
|
SpaceOccupied += entry.DataFileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除指定缓存
|
||||||
|
/// </summary>
|
||||||
|
internal void RemoveEntry(string bundleGUID)
|
||||||
|
{
|
||||||
|
if (_caches.TryGetValue(bundleGUID, out BundleCacheEntry entry))
|
||||||
|
{
|
||||||
|
_caches.Remove(bundleGUID);
|
||||||
|
_dataFilePathMapping.Remove(bundleGUID);
|
||||||
|
_infoFilePathMapping.Remove(bundleGUID);
|
||||||
|
SpaceOccupied -= entry.DataFileSize;
|
||||||
|
entry.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetHashFolderName(string fileHash)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fileHash))
|
||||||
|
throw new YooInternalException();
|
||||||
|
|
||||||
|
if (fileHash.Length <= HashFolderLength)
|
||||||
|
return fileHash;
|
||||||
|
return fileHash.Substring(0, HashFolderLength);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: dc67a10bc17d3cf4f960251012a2387b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
internal class BundleCacheDefine
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 数据文件名称
|
||||||
|
/// </summary>
|
||||||
|
public const string BundleDataFileName = "__data";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 信息文件名称
|
||||||
|
/// </summary>
|
||||||
|
public const string BundleInfoFileName = "__info";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: af5c9bfdcfeff44468bc464f7daeb946
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,17 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
internal class FileCacheEntry
|
internal class BundleCacheEntry : ICacheEntry
|
||||||
{
|
{
|
||||||
public string InfoFilePath { private set; get; }
|
public string BundleGUID { get; private set; }
|
||||||
public string DataFilePath { private set; get; }
|
public string InfoFilePath { get; private set; }
|
||||||
public uint DataFileCRC { private set; get; }
|
public string DataFilePath { get; private set; }
|
||||||
public long DataFileSize { private set; get; }
|
public uint DataFileCRC { get; private set; }
|
||||||
|
public long DataFileSize { get; private set; }
|
||||||
|
|
||||||
public FileCacheEntry(string infoFilePath, string dataFilePath, uint dataFileCRC, long dataFileSize)
|
public BundleCacheEntry(string bundleGUID, string infoFilePath, string dataFilePath, uint dataFileCRC, long dataFileSize)
|
||||||
{
|
{
|
||||||
|
BundleGUID = bundleGUID;
|
||||||
InfoFilePath = infoFilePath;
|
InfoFilePath = infoFilePath;
|
||||||
DataFilePath = dataFilePath;
|
DataFilePath = dataFilePath;
|
||||||
DataFileCRC = dataFileCRC;
|
DataFileCRC = dataFileCRC;
|
||||||
@@ -19,22 +21,23 @@ namespace YooAsset
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修正内容
|
/// 移动文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Modify(string dataFilePath)
|
public void MoveFile(string destFilePath)
|
||||||
{
|
{
|
||||||
DataFilePath = dataFilePath;
|
File.Move(DataFilePath, destFilePath);
|
||||||
|
DataFilePath = destFilePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除记录文件
|
/// 删除记录文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DeleteFolder()
|
public bool Delete()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string directory = Path.GetDirectoryName(InfoFilePath);
|
string directory = Path.GetDirectoryName(InfoFilePath);
|
||||||
DirectoryInfo directoryInfo = new DirectoryInfo(directory);
|
var directoryInfo = new DirectoryInfo(directory);
|
||||||
if (directoryInfo.Exists)
|
if (directoryInfo.Exists)
|
||||||
{
|
{
|
||||||
directoryInfo.Delete(true);
|
directoryInfo.Delete(true);
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 440983279a9c8f04789434e24f0ff10b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 51dceae1fa3604044afd0409fe8d6c84
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 00391c6495efccb42bacdd0bd9cfa374
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace YooAsset
|
namespace YooAsset
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,8 @@ namespace YooAsset
|
|||||||
Done,
|
Done,
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly CacheFileSystem _fileSystem;
|
private readonly BundleCache _cache;
|
||||||
|
private readonly bool _appendFileExtension;
|
||||||
private IEnumerator<string> _filesEnumerator = null;
|
private IEnumerator<string> _filesEnumerator = null;
|
||||||
private double _verifyStartTime;
|
private double _verifyStartTime;
|
||||||
private ESteps _steps = ESteps.None;
|
private ESteps _steps = ESteps.None;
|
||||||
@@ -26,14 +28,14 @@ namespace YooAsset
|
|||||||
public readonly List<VerifyFileInfo> Result = new List<VerifyFileInfo>(5000);
|
public readonly List<VerifyFileInfo> Result = new List<VerifyFileInfo>(5000);
|
||||||
|
|
||||||
|
|
||||||
internal SearchCacheFilesOperation(CacheFileSystem fileSystem)
|
internal SearchCacheFilesOperation(BundleCache cache)
|
||||||
{
|
{
|
||||||
_fileSystem = fileSystem;
|
_cache = cache;
|
||||||
|
_appendFileExtension = cache.AppendFileExtension;
|
||||||
}
|
}
|
||||||
internal override void InternalStart()
|
internal override void InternalStart()
|
||||||
{
|
{
|
||||||
_steps = ESteps.Prepare;
|
_steps = ESteps.Prepare;
|
||||||
_verifyStartTime = TimeUtility.RealtimeSinceStartup;
|
|
||||||
}
|
}
|
||||||
internal override void InternalUpdate()
|
internal override void InternalUpdate()
|
||||||
{
|
{
|
||||||
@@ -42,13 +44,18 @@ namespace YooAsset
|
|||||||
|
|
||||||
if (_steps == ESteps.Prepare)
|
if (_steps == ESteps.Prepare)
|
||||||
{
|
{
|
||||||
string rootDirectory = _fileSystem.GetCacheBundleFilesRoot();
|
if (Directory.Exists(_cache.RootPath))
|
||||||
if (Directory.Exists(rootDirectory))
|
|
||||||
{
|
{
|
||||||
var directories = Directory.EnumerateDirectories(rootDirectory);
|
var directories = Directory.EnumerateDirectories(_cache.RootPath);
|
||||||
_filesEnumerator = directories.GetEnumerator();
|
_filesEnumerator = directories.GetEnumerator();
|
||||||
|
_verifyStartTime = TimeUtility.RealtimeSinceStartup;
|
||||||
|
_steps = ESteps.SearchFiles;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
}
|
}
|
||||||
_steps = ESteps.SearchFiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_steps == ESteps.SearchFiles)
|
if (_steps == ESteps.SearchFiles)
|
||||||
@@ -56,8 +63,11 @@ namespace YooAsset
|
|||||||
if (SearchFiles())
|
if (SearchFiles())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
_filesEnumerator.Dispose();
|
||||||
|
_filesEnumerator = null;
|
||||||
|
|
||||||
_steps = ESteps.Done;
|
_steps = ESteps.Done;
|
||||||
Status = EOperationStatus.Succeed;
|
Status = EOperationStatus.Succeeded;
|
||||||
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
|
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
|
||||||
YooLogger.Log($"Search cache files elapsed time {costTime:f1} seconds");
|
YooLogger.Log($"Search cache files elapsed time {costTime:f1} seconds");
|
||||||
}
|
}
|
||||||
@@ -65,9 +75,6 @@ namespace YooAsset
|
|||||||
|
|
||||||
private bool SearchFiles()
|
private bool SearchFiles()
|
||||||
{
|
{
|
||||||
if (_filesEnumerator == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
bool isFindItem;
|
bool isFindItem;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
@@ -80,25 +87,25 @@ namespace YooAsset
|
|||||||
foreach (var chidDirectory in childDirectories)
|
foreach (var chidDirectory in childDirectories)
|
||||||
{
|
{
|
||||||
string bundleGUID = Path.GetFileName(chidDirectory);
|
string bundleGUID = Path.GetFileName(chidDirectory);
|
||||||
if (_fileSystem.IsRecordBundleFile(bundleGUID))
|
if (_cache.IsCached(bundleGUID))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 创建验证元素类
|
// 创建验证元素类
|
||||||
string fileRootPath = chidDirectory;
|
string fileRootPath = chidDirectory;
|
||||||
string dataFilePath = $"{fileRootPath}/{DefaultCacheFileSystemDefine.BundleDataFileName}";
|
string dataFilePath = PathUtility.Combine(fileRootPath, BundleCacheDefine.BundleDataFileName);
|
||||||
string infoFilePath = $"{fileRootPath}/{DefaultCacheFileSystemDefine.BundleInfoFileName}";
|
string infoFilePath = PathUtility.Combine(fileRootPath, BundleCacheDefine.BundleInfoFileName);
|
||||||
|
|
||||||
// 存储的数据文件追加文件格式
|
// 存储的数据文件追加文件格式
|
||||||
if (_fileSystem.AppendFileExtension)
|
if (_appendFileExtension)
|
||||||
{
|
{
|
||||||
string dataFileExtension = FindDataFileExtension(chidDirectory);
|
string realFilePath = FindRealDataFilePath(chidDirectory);
|
||||||
if (string.IsNullOrEmpty(dataFileExtension) == false)
|
if (string.IsNullOrEmpty(realFilePath) == false)
|
||||||
{
|
{
|
||||||
dataFilePath += dataFileExtension;
|
dataFilePath = realFilePath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var element = new VerifyFileInfo(_fileSystem.PackageName, bundleGUID, fileRootPath, dataFilePath, infoFilePath);
|
var element = new VerifyFileInfo(_cache.PackageName, bundleGUID, fileRootPath, dataFilePath, infoFilePath);
|
||||||
Result.Add(element);
|
Result.Add(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,17 +115,11 @@ namespace YooAsset
|
|||||||
|
|
||||||
return isFindItem;
|
return isFindItem;
|
||||||
}
|
}
|
||||||
private string FindDataFileExtension(string directory)
|
private string FindRealDataFilePath(string directory)
|
||||||
{
|
{
|
||||||
string dataFileExtension = string.Empty;
|
string searchPattern = BundleCacheDefine.BundleDataFileName + "*";
|
||||||
string searchPattern = DefaultCacheFileSystemDefine.BundleDataFileName + "*";
|
var searchFiles = Directory.EnumerateFiles(directory, searchPattern);
|
||||||
var dataFiles = Directory.EnumerateFiles(directory, searchPattern);
|
return searchFiles.FirstOrDefault();
|
||||||
foreach (var filePath in dataFiles)
|
|
||||||
{
|
|
||||||
dataFileExtension = Path.GetExtension(filePath);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return dataFileExtension;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace YooAsset
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 缓存文件验证(线程版)
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class VerifyCacheFilesOperation : AsyncOperationBase
|
||||||
|
{
|
||||||
|
private enum ESteps
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
InitVerify,
|
||||||
|
UpdateVerify,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly BundleCache _cache;
|
||||||
|
private readonly EFileVerifyLevel _verifyLevel;
|
||||||
|
private readonly int _fileVerifyMaxConcurrency;
|
||||||
|
private readonly List<VerifyFileInfo> _waitingList;
|
||||||
|
private List<VerifyFileInfo> _verifyingList;
|
||||||
|
private int _verifyMaxNum;
|
||||||
|
private int _verifyTotalCount;
|
||||||
|
private double _verifyStartTime;
|
||||||
|
private int _succeedCount;
|
||||||
|
private int _failedCount;
|
||||||
|
private ESteps _steps = ESteps.None;
|
||||||
|
|
||||||
|
|
||||||
|
internal VerifyCacheFilesOperation(BundleCache cache, EFileVerifyLevel verifyLevel, int fileVerifyMaxConcurrency, List<VerifyFileInfo> elements)
|
||||||
|
{
|
||||||
|
_cache = cache;
|
||||||
|
_verifyLevel = verifyLevel;
|
||||||
|
_fileVerifyMaxConcurrency = fileVerifyMaxConcurrency;
|
||||||
|
_waitingList = elements;
|
||||||
|
}
|
||||||
|
internal override void InternalStart()
|
||||||
|
{
|
||||||
|
_steps = ESteps.InitVerify;
|
||||||
|
}
|
||||||
|
internal override void InternalUpdate()
|
||||||
|
{
|
||||||
|
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_steps == ESteps.InitVerify)
|
||||||
|
{
|
||||||
|
// 设置同时验证的最大数
|
||||||
|
int processorCount = Environment.ProcessorCount * 2 + 1;
|
||||||
|
_verifyMaxNum = Math.Min(processorCount, _fileVerifyMaxConcurrency);
|
||||||
|
if (_verifyMaxNum < 1)
|
||||||
|
_verifyMaxNum = 1;
|
||||||
|
|
||||||
|
YooLogger.Log($"Verify max concurrency : {_verifyMaxNum}");
|
||||||
|
_verifyingList = new List<VerifyFileInfo>(_verifyMaxNum);
|
||||||
|
_verifyStartTime = TimeUtility.RealtimeSinceStartup;
|
||||||
|
_verifyTotalCount = _waitingList.Count;
|
||||||
|
_steps = ESteps.UpdateVerify;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_steps == ESteps.UpdateVerify)
|
||||||
|
{
|
||||||
|
// 检测校验结果
|
||||||
|
for (int i = _verifyingList.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var verifyElement = _verifyingList[i];
|
||||||
|
int result = verifyElement.Result;
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
_verifyingList.RemoveAt(i);
|
||||||
|
if (verifyElement.Result == (int)EFileVerifyResult.Succeed)
|
||||||
|
{
|
||||||
|
_succeedCount++;
|
||||||
|
var cacheEntry = new BundleCacheEntry(verifyElement.BundleGUID, verifyElement.InfoFilePath, verifyElement.DataFilePath, verifyElement.DataFileCRC, verifyElement.DataFileSize);
|
||||||
|
_cache.AddEntry(verifyElement.BundleGUID, cacheEntry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_failedCount++;
|
||||||
|
YooLogger.Warning($"Failed to verify file {verifyElement.Result} and delete files : {verifyElement.FolderPath}");
|
||||||
|
verifyElement.DeleteFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Progress = GetProgress();
|
||||||
|
if (_waitingList.Count == 0 && _verifyingList.Count == 0)
|
||||||
|
{
|
||||||
|
_steps = ESteps.Done;
|
||||||
|
Status = EOperationStatus.Succeeded;
|
||||||
|
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
|
||||||
|
YooLogger.Log($"Verify cache files elapsed time {costTime:f1} seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = _waitingList.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (IsBusy)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (_verifyingList.Count >= _verifyMaxNum)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var element = _waitingList[i];
|
||||||
|
bool succeed = ThreadPool.QueueUserWorkItem(new WaitCallback(VerifyFileInThread), element);
|
||||||
|
if (succeed == false)
|
||||||
|
VerifyFileInThread(element);
|
||||||
|
|
||||||
|
_waitingList.RemoveAt(i);
|
||||||
|
_verifyingList.Add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private float GetProgress()
|
||||||
|
{
|
||||||
|
if (_verifyTotalCount == 0)
|
||||||
|
return 1f;
|
||||||
|
return (float)(_succeedCount + _failedCount) / _verifyTotalCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证缓存文件(子线程内操作)
|
||||||
|
private void VerifyFileInThread(object obj)
|
||||||
|
{
|
||||||
|
VerifyFileInfo element = (VerifyFileInfo)obj;
|
||||||
|
int verifyResult = (int)VerifyFile(element, _verifyLevel);
|
||||||
|
element.Result = verifyResult;
|
||||||
|
}
|
||||||
|
private EFileVerifyResult VerifyFile(VerifyFileInfo element, EFileVerifyLevel verifyLevel)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (File.Exists(element.InfoFilePath) == false)
|
||||||
|
return EFileVerifyResult.InfoFileNotExisted;
|
||||||
|
if (File.Exists(element.DataFilePath) == false)
|
||||||
|
return EFileVerifyResult.DataFileNotExisted;
|
||||||
|
|
||||||
|
// 解析信息文件填充验证数据
|
||||||
|
// 注意:验证数据在后续流程会被使用。
|
||||||
|
byte[] binaryData = FileUtility.ReadAllBytes(element.InfoFilePath);
|
||||||
|
BufferReader buffer = new BufferReader(binaryData);
|
||||||
|
uint dataFileCRC = buffer.ReadUInt32();
|
||||||
|
long dataFileSize = buffer.ReadInt64();
|
||||||
|
element.SetDataFileInfo(dataFileCRC, dataFileSize);
|
||||||
|
|
||||||
|
if (verifyLevel == EFileVerifyLevel.Low)
|
||||||
|
{
|
||||||
|
return EFileVerifyResult.Succeed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (verifyLevel == EFileVerifyLevel.Middle)
|
||||||
|
return FileVerifyTools.FileVerify(element.DataFilePath, dataFileSize, 0);
|
||||||
|
else if (verifyLevel == EFileVerifyLevel.High)
|
||||||
|
return FileVerifyTools.FileVerify(element.DataFilePath, dataFileSize, dataFileCRC);
|
||||||
|
else
|
||||||
|
throw new System.NotImplementedException(verifyLevel.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
YooLogger.Error($"File verify exception : {ex.Message}");
|
||||||
|
return EFileVerifyResult.Exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user