mirror of
https://github.com/tuyoogame/YooAsset.git
synced 2026-05-28 19:48:47 +00:00
Compare commits
31 Commits
6b23927f71
...
3.0.2-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f631d67db3 | ||
|
|
a2fa8ff6fc | ||
|
|
477954377d | ||
|
|
513c1d0b18 | ||
|
|
289dee326b | ||
|
|
d5a9b9f0f4 | ||
|
|
aff50317cb | ||
|
|
9e969d34f6 | ||
|
|
926f72d129 | ||
|
|
72c5a9588e | ||
|
|
af7aecbf0b | ||
|
|
d337a86e68 | ||
|
|
9c05f3514e | ||
|
|
b31bb1bcfb | ||
|
|
92e34fb8b5 | ||
|
|
687dc1d9e9 | ||
|
|
7842235af6 | ||
|
|
0142828b91 | ||
|
|
a36a7cc6d2 | ||
|
|
485ed5198e | ||
|
|
2170156216 | ||
|
|
99b975554a | ||
|
|
2f8355c3f4 | ||
|
|
f05f794461 | ||
|
|
a5ec8e8eb9 | ||
|
|
c0c3bb406d | ||
|
|
656b8f18e2 | ||
|
|
429a7395ca | ||
|
|
f8f2dd8faf | ||
|
|
45ecd8baff | ||
|
|
322c4a9847 |
@@ -2,6 +2,101 @@
|
||||
|
||||
All notable changes to this package will be documented in this file.
|
||||
|
||||
## [3.0.2-beta] - 2026-05-28
|
||||
|
||||
### Added
|
||||
|
||||
- 新增 `WebNetworkFileSystem` 文件系统
|
||||
|
||||
用于统一 WebGL 远程加载和 Mini Game 平台资源加载流程,并通过 `IWebPlatformStrategy` 适配不同平台的 AssetBundle 请求、提取和卸载行为。
|
||||
|
||||
- 新增 Web 文件系统对 `RawBundle` 和 `ArchiveBundle` 的加载支持
|
||||
|
||||
`WebServerFileSystem` 和 `WebNetworkFileSystem` 支持在 WebGL 平台加载原始文件包和归档资源包。
|
||||
|
||||
- 新增加密 `ArchiveBundle` 构建和加载支持
|
||||
|
||||
`ArchiveFileBuildPipeline` 支持加密归档资源包,运行时可通过 `EFileSystemParameter.ArchiveBundleDecryptor` 配置对应解密器。
|
||||
|
||||
- 新增 `IBundleUnpackPolicy` 内置资源包解包策略
|
||||
|
||||
支持根据资源包名称、文件名、类型、加密状态和标签,自定义哪些内置资源包需要解包。
|
||||
|
||||
- 新增 `IBuiltinFileAccessor` 内置文件访问器
|
||||
|
||||
支持为 `StreamingAssets` 文件提供自定义存在检测和字节读取能力,方便 Android 等平台接入第三方同步读取方案。
|
||||
|
||||
- 新增 Bundle Collector 资源搜索功能
|
||||
|
||||
支持在收集器窗口输入或拖入资源路径,定位资源所在的分组和收集器,并高亮命中的资源配置。
|
||||
|
||||
- 新增 Mini Game 平台示例
|
||||
|
||||
补充 OPPO、vivo、快手小游戏文件系统示例。
|
||||
|
||||
### Changed
|
||||
|
||||
- WebGL 运行模式文件系统结构调整
|
||||
|
||||
移除旧版 `WebGameFileSystem` 和 `WebRemoteFileSystem`,相关能力已合并到新的文件系统 `WebNetworkFileSystem`。
|
||||
|
||||
### Fixed
|
||||
|
||||
- 修复微信小游戏示例宏兼容问题
|
||||
|
||||
微信小游戏文件系统同时支持 `WEIXINMINIGAME` 和 `UNITY_WECHATMINIGAME` 宏。
|
||||
|
||||
## [3.0.1-beta] - 2026-05-19
|
||||
|
||||
### Added
|
||||
|
||||
- 新增归档文件构建管线 `ArchiveFileBuildPipeline`
|
||||
|
||||
支持将同一资源包内的多个原始文件合并为 `ArchiveBundle`,并提供构建参数、构建任务、编辑器构建界面和运行时加载支持。
|
||||
|
||||
- 新增 `EnsureBundleFileOperation` 操作
|
||||
|
||||
用于确保资源包文件已就绪,并通过 `EnsureBundleFileOperation.Detail` 获取资源包名称、本地文件路径、资源包类型和加密状态。
|
||||
|
||||
**代码示例**
|
||||
|
||||
```csharp
|
||||
var package = YooAssets.GetPackage("DefaultPackage");
|
||||
|
||||
// 确保资源所在的资源包文件已经就绪。
|
||||
// 如果资源包未缓存,会自动触发下载或解压流程。
|
||||
var operation = package.EnsureBundleFileAsync(new EnsureBundleFileOptions("asset_location"));
|
||||
yield return operation;
|
||||
|
||||
if (operation.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
var detail = operation.Detail;
|
||||
string bundleName = detail.BundleName;
|
||||
string bundleFilePath = detail.BundleFilePath;
|
||||
int bundleType = detail.BundleType;
|
||||
bool isEncrypted = detail.IsEncrypted;
|
||||
|
||||
UnityEngine.Debug.Log($"BundleName: {bundleName}");
|
||||
UnityEngine.Debug.Log($"BundleFilePath: {bundleFilePath}");
|
||||
UnityEngine.Debug.Log($"BundleType: {bundleType}");
|
||||
UnityEngine.Debug.Log($"IsEncrypted: {isEncrypted}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityEngine.Debug.LogError(operation.Error);
|
||||
}
|
||||
```
|
||||
|
||||
- 新增编辑器模拟模式下的 Bundle Cache 标记文件机制
|
||||
|
||||
通过记录缓存状态,提升虚拟下载模式下缓存扫描、恢复和清理的可靠性。
|
||||
|
||||
### Changed
|
||||
|
||||
- `LoadRawFile` API 重命名为 `LoadBundleFile`
|
||||
|
||||
对应句柄由 `RawFileHandle` 重命名为 `BundleFileHandle`,语义从“原生文件”调整为“资源包文件”。
|
||||
|
||||
## [3.0.0-beta] - 2026-05-09
|
||||
|
||||
beta released.
|
||||
|
||||
@@ -27,14 +27,6 @@ namespace YooAsset.Editor
|
||||
/// <inheritdoc />
|
||||
protected override void CheckBuildParametersCore()
|
||||
{
|
||||
// ArchiveBundle 不支持资源包加密
|
||||
if (BundleEncryptor != null)
|
||||
{
|
||||
string message = BuildLogger.GetErrorMessage(ErrorCode.BundleEncryptionNotSupported,
|
||||
$"ArchiveFileBuildPipeline does not support bundle encryption. Please remove the BundleEncryptor configuration.");
|
||||
throw new NotSupportedException(message);
|
||||
}
|
||||
|
||||
// 校验文件对齐参数范围
|
||||
if (FileAlignment < 0 || FileAlignment > MaxFileAlignment)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace YooAsset.Editor
|
||||
/// <summary>
|
||||
/// 构建资源包类型下拉框
|
||||
/// </summary>
|
||||
protected DropdownField _buildBundleTypeField;
|
||||
protected PopupField<string> _buildBundleTypeField;
|
||||
|
||||
|
||||
public override void CreateView(VisualElement parent)
|
||||
@@ -56,8 +56,8 @@ namespace YooAsset.Editor
|
||||
SetBuildVersionField(_buildVersionField);
|
||||
|
||||
// 构建资源包类型
|
||||
_buildBundleTypeField = Root.Q<DropdownField>("BuildBundleType");
|
||||
SetBuildBundleTypeField(_buildBundleTypeField);
|
||||
var buildBundleTypeContainer = Root.Q<VisualElement>("BuildBundleType");
|
||||
_buildBundleTypeField = CreateBuildBundleTypeField(buildBundleTypeContainer);
|
||||
|
||||
// 构建按钮
|
||||
var buildButton = Root.Q<Button>("Build");
|
||||
@@ -104,7 +104,7 @@ namespace YooAsset.Editor
|
||||
EditorUtility.RevealInFinder(buildResult.OutputPackageDirectory);
|
||||
}
|
||||
|
||||
private void SetBuildBundleTypeField(DropdownField dropdownField)
|
||||
private PopupField<string> CreateBuildBundleTypeField(VisualElement container)
|
||||
{
|
||||
var bundleTypes = Enum.GetValues(typeof(EBundleType))
|
||||
.Cast<EBundleType>()
|
||||
@@ -112,10 +112,17 @@ namespace YooAsset.Editor
|
||||
.Select(type => type.ToString())
|
||||
.ToList();
|
||||
|
||||
dropdownField.choices = bundleTypes;
|
||||
dropdownField.SetValueWithoutNotify(EBundleType.VirtualAssetBundle.ToString());
|
||||
dropdownField.style.width = StyleWidth;
|
||||
UIElementsTools.SetElementLabelMinWidth(dropdownField, LabelMinWidth);
|
||||
int defaultIndex = bundleTypes.IndexOf(EBundleType.VirtualAssetBundle.ToString());
|
||||
if (defaultIndex < 0)
|
||||
defaultIndex = 0;
|
||||
|
||||
var popupField = new PopupField<string>(bundleTypes, defaultIndex);
|
||||
popupField.label = "Build Bundle Type";
|
||||
popupField.style.width = StyleWidth;
|
||||
|
||||
container.Add(popupField);
|
||||
UIElementsTools.SetElementLabelMinWidth(popupField, LabelMinWidth);
|
||||
return popupField;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<ui:VisualElement name="BuildContainer">
|
||||
<ui:TextField picking-mode="Ignore" label="Build Output" name="BuildOutput" />
|
||||
<ui:TextField picking-mode="Ignore" label="Build Version" name="BuildVersion" />
|
||||
<ui:DropdownField label="Build Bundle Type" name="BuildBundleType" />
|
||||
<ui:VisualElement name="BuildBundleType" />
|
||||
<ui:VisualElement name="ExtensionContainer" />
|
||||
<ui:Button text="Click Build" display-tooltip-when-elided="true" name="Build" style="height: 50px; background-color: rgb(40, 106, 42); margin-top: 10px;" />
|
||||
</ui:VisualElement>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if TUANJIE_1_8_OR_NEWER
|
||||
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bc4cd72a390c1054a99c49a7c501c8f6
|
||||
guid: 07c436f0dfa2bcf43b12628aed2aa91c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,52 @@
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 收集资源搜索结果
|
||||
/// </summary>
|
||||
public class CollectAssetSearchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 命中的收集器分组
|
||||
/// </summary>
|
||||
public BundleCollectorGroup Group { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 命中的收集器分组索引
|
||||
/// </summary>
|
||||
public int GroupIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 命中的收集器
|
||||
/// </summary>
|
||||
public BundleCollector Collector { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 命中的收集器索引
|
||||
/// </summary>
|
||||
public int CollectorIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 命中的资源路径
|
||||
/// </summary>
|
||||
public string AssetPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构建收集资源搜索结果
|
||||
/// </summary>
|
||||
/// <param name="group">命中的收集器分组</param>
|
||||
/// <param name="groupIndex">命中的收集器分组索引</param>
|
||||
/// <param name="collector">命中的收集器</param>
|
||||
/// <param name="collectorIndex">命中的收集器索引</param>
|
||||
/// <param name="assetPath">命中的资源路径</param>
|
||||
public CollectAssetSearchResult(BundleCollectorGroup group, int groupIndex,
|
||||
BundleCollector collector, int collectorIndex, string assetPath)
|
||||
{
|
||||
Group = group;
|
||||
GroupIndex = groupIndex;
|
||||
Collector = collector;
|
||||
CollectorIndex = collectorIndex;
|
||||
AssetPath = assetPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c79e03ac8bcbef4aa0e2eede5bf63fb
|
||||
guid: ff1a09e6aca8b104c9b256885b06130f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,148 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 收集资源搜索工具类
|
||||
/// </summary>
|
||||
public static class CollectAssetSearchUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证搜索路径
|
||||
/// </summary>
|
||||
/// <param name="input">搜索路径</param>
|
||||
/// <returns>搜索路径错误类型</returns>
|
||||
public static ECollectAssetSearchError ValidateSearchPath(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return ECollectAssetSearchError.InputPathEmpty;
|
||||
|
||||
if (input.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase) == false)
|
||||
return ECollectAssetSearchError.InputPathMissingAssetsPrefix;
|
||||
|
||||
if (input.EndsWith("/"))
|
||||
return ECollectAssetSearchError.InputPathEndsWithSlash;
|
||||
|
||||
string fileName = System.IO.Path.GetFileName(input);
|
||||
if (fileName.Contains(".") == false)
|
||||
return ECollectAssetSearchError.InputPathMissingExtension;
|
||||
|
||||
if (AssetDatabase.IsValidFolder(input))
|
||||
return ECollectAssetSearchError.InputPathIsFolder;
|
||||
|
||||
string guid = AssetDatabase.AssetPathToGUID(input);
|
||||
if (string.IsNullOrEmpty(guid))
|
||||
return ECollectAssetSearchError.AssetPathNotExists;
|
||||
|
||||
return ECollectAssetSearchError.None;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取搜索路径错误提示信息
|
||||
/// </summary>
|
||||
/// <param name="error">搜索路径错误类型</param>
|
||||
/// <param name="input">搜索路径</param>
|
||||
/// <returns>错误提示信息</returns>
|
||||
public static string GetSearchPathErrorMessage(ECollectAssetSearchError error, string input)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case ECollectAssetSearchError.InputPathEmpty:
|
||||
return "Please enter an asset path.";
|
||||
case ECollectAssetSearchError.InputPathMissingAssetsPrefix:
|
||||
return "Path must start with Assets/.";
|
||||
case ECollectAssetSearchError.InputPathEndsWithSlash:
|
||||
return "Please enter a file path. Do not end with /.";
|
||||
case ECollectAssetSearchError.InputPathMissingExtension:
|
||||
return "Path is missing a file extension (e.g. .prefab, .png, .mat).";
|
||||
case ECollectAssetSearchError.InputPathIsFolder:
|
||||
return "Please enter an asset file path, not a folder path.";
|
||||
case ECollectAssetSearchError.AssetPathNotExists:
|
||||
return $"Asset not found: {input}";
|
||||
default:
|
||||
return "Invalid input format.";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在指定 Package 中搜索资源路径,找到第一个命中结果即返回
|
||||
/// </summary>
|
||||
/// <param name="package">搜索的资源包裹</param>
|
||||
/// <param name="assetPath">资源路径</param>
|
||||
/// <returns>搜索结果,如果未找到返回 null</returns>
|
||||
public static CollectAssetSearchResult SearchAssetPath(BundleCollectorPackage package, string assetPath)
|
||||
{
|
||||
if (ValidateSearchPath(assetPath) != ECollectAssetSearchError.None)
|
||||
return null;
|
||||
|
||||
IAssetIgnoreRule ignoreRule = BundleCollectorSettingData.GetAssetIgnoreRuleInstance(package.IgnoreRuleName);
|
||||
var command = new CollectCommand(package.PackageName, ignoreRule);
|
||||
command.SetFlag(ECollectFlags.IgnoreGetDependencies, true);
|
||||
command.UniqueBundleName = BundleCollectorSettingData.Setting.UniqueBundleName;
|
||||
command.EnableAddressable = package.EnableAddressable;
|
||||
command.SupportExtensionless = package.SupportExtensionless;
|
||||
command.LocationToLower = package.LocationToLower;
|
||||
command.IncludeAssetGUID = package.IncludeAssetGUID;
|
||||
command.AutoCollectShaders = package.AutoCollectShaders;
|
||||
|
||||
for (int groupIndex = 0; groupIndex < package.Groups.Count; groupIndex++)
|
||||
{
|
||||
var group = package.Groups[groupIndex];
|
||||
for (int collectIndex = 0; collectIndex < group.Collectors.Count; collectIndex++)
|
||||
{
|
||||
var collector = group.Collectors[collectIndex];
|
||||
|
||||
// 判断收集器是否可能收集指定资源
|
||||
if (IsCandidateCollector(collector, assetPath) == false)
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
// 检测配置是否有效
|
||||
collector.CheckConfigError();
|
||||
|
||||
// 收集有效资源信息
|
||||
var collectAssets = collector.GetAllCollectAssets(command, group);
|
||||
foreach (var collectAsset in collectAssets)
|
||||
{
|
||||
if (string.Equals(collectAsset.AssetInfo.AssetPath, assetPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new CollectAssetSearchResult(group, groupIndex, collector, collectIndex, assetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Invalid collector : {collector.CollectPath}, error: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 未找到匹配资源
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断收集器是否可能收集指定资源
|
||||
/// </summary>
|
||||
/// <param name="collector">收集器</param>
|
||||
/// <param name="assetPath">资源路径</param>
|
||||
/// <returns>如果收集器可能收集该资源返回 true</returns>
|
||||
private static bool IsCandidateCollector(BundleCollector collector, string assetPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(collector.CollectPath))
|
||||
return false;
|
||||
|
||||
if (AssetDatabase.IsValidFolder(collector.CollectPath))
|
||||
{
|
||||
string folderPath = collector.CollectPath.TrimEnd('/') + "/";
|
||||
return assetPath.StartsWith(folderPath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// 注意:资源收集器也可能直接配置的单个资源路径
|
||||
return string.Equals(assetPath, collector.CollectPath, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1cfc5979929f8cd43b58e1fcc541eaa8
|
||||
guid: 6c8bc2931bda7884198893f1d2e8f364
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
namespace YooAsset.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 搜索错误类型
|
||||
/// </summary>
|
||||
public enum ECollectAssetSearchError
|
||||
{
|
||||
/// <summary>
|
||||
/// 无错误
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// 输入路径为空
|
||||
/// </summary>
|
||||
InputPathEmpty,
|
||||
|
||||
/// <summary>
|
||||
/// 输入路径缺少 Assets/ 路径前缀
|
||||
/// </summary>
|
||||
InputPathMissingAssetsPrefix,
|
||||
|
||||
/// <summary>
|
||||
/// 输入路径以斜杠结尾
|
||||
/// </summary>
|
||||
InputPathEndsWithSlash,
|
||||
|
||||
/// <summary>
|
||||
/// 输入路径缺少文件扩展名
|
||||
/// </summary>
|
||||
InputPathMissingExtension,
|
||||
|
||||
/// <summary>
|
||||
/// 输入路径的是文件夹路径
|
||||
/// </summary>
|
||||
InputPathIsFolder,
|
||||
|
||||
/// <summary>
|
||||
/// 资源文件不存在
|
||||
/// </summary>
|
||||
AssetPathNotExists,
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ef65b981b91200439b05f29435e7c92
|
||||
guid: 9a2c8a878d6041b4d8bad2a2d2f1896d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -60,21 +60,22 @@ namespace YooAsset.Editor
|
||||
/// <returns>如果收集器配置有效返回 true</returns>
|
||||
public bool IsValid()
|
||||
{
|
||||
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(CollectPath) == null)
|
||||
string assetGUID = AssetDatabase.AssetPathToGUID(CollectPath);
|
||||
if (string.IsNullOrEmpty(assetGUID))
|
||||
return false;
|
||||
|
||||
if (CollectorType == ECollectorType.None)
|
||||
return false;
|
||||
|
||||
if (BundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false)
|
||||
return false;
|
||||
|
||||
if (BundleCollectorSettingData.HasBundlePackRuleName(PackRuleName) == false)
|
||||
return false;
|
||||
|
||||
if (BundleCollectorSettingData.HasAssetFilterRuleName(FilterRuleName) == false)
|
||||
return false;
|
||||
|
||||
if (BundleCollectorSettingData.HasAddressRuleName(AddressRuleName) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace YooAsset.Editor
|
||||
window.minSize = new Vector2(800, 600);
|
||||
}
|
||||
|
||||
private const string PlaceholderClass = "search-placeholder";
|
||||
|
||||
private Button _saveButton;
|
||||
private List<string> _collectorTypeList;
|
||||
private List<RuleDisplayName> _groupActiveRuleList;
|
||||
@@ -35,6 +37,11 @@ namespace YooAsset.Editor
|
||||
|
||||
private VisualElement _helpBoxContainer;
|
||||
|
||||
private ToolbarSearchField _searchField;
|
||||
private TextField _searchTextField;
|
||||
private Button _searchButton;
|
||||
private Label _searchResultLabel;
|
||||
|
||||
private Button _globalSettingsButton;
|
||||
private Button _packageSettingsButton;
|
||||
|
||||
@@ -66,6 +73,8 @@ namespace YooAsset.Editor
|
||||
private ScrollView _collectorScrollView;
|
||||
private PopupField<RuleDisplayName> _activeRulePopupField;
|
||||
|
||||
private string _highlightAssetPath;
|
||||
private int _highlightCollectorIndex = -1;
|
||||
private int _lastModifyPackageIndex = 0;
|
||||
private int _lastModifyGroupIndex = 0;
|
||||
private bool _showGlobalSettings = false;
|
||||
@@ -219,6 +228,44 @@ namespace YooAsset.Editor
|
||||
_saveButton = root.Q<Button>("SaveButton");
|
||||
_saveButton.clicked += OnSaveButtonClicked;
|
||||
|
||||
// 搜索相关
|
||||
_searchField = root.Q<ToolbarSearchField>("SearchField");
|
||||
_searchTextField = _searchField.Q<TextField>();
|
||||
_searchTextField.RegisterCallback<FocusInEvent>(evt =>
|
||||
{
|
||||
if (_searchTextField.ClassListContains(PlaceholderClass))
|
||||
{
|
||||
_searchField.value = string.Empty;
|
||||
ClearSearchPlaceholder();
|
||||
}
|
||||
});
|
||||
_searchTextField.RegisterCallback<FocusOutEvent>(evt =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(_searchField.value))
|
||||
ApplySearchPlaceholder();
|
||||
});
|
||||
_searchField.RegisterCallback<DragUpdatedEvent>(evt =>
|
||||
{
|
||||
if (DragAndDrop.objectReferences.Length > 0)
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
|
||||
});
|
||||
_searchField.RegisterCallback<DragPerformEvent>(evt =>
|
||||
{
|
||||
if (DragAndDrop.objectReferences.Length > 0)
|
||||
{
|
||||
string assetPath = AssetDatabase.GetAssetPath(DragAndDrop.objectReferences[0]);
|
||||
if (string.IsNullOrEmpty(assetPath) == false)
|
||||
{
|
||||
_searchField.value = assetPath;
|
||||
ClearSearchPlaceholder();
|
||||
}
|
||||
}
|
||||
});
|
||||
ApplySearchPlaceholder();
|
||||
_searchButton = root.Q<Button>("SearchButton");
|
||||
_searchButton.clicked += OnSearchButtonClicked;
|
||||
_searchResultLabel = root.Q<Label>("SearchResultLabel");
|
||||
|
||||
// 包裹容器
|
||||
_packageContainer = root.Q("PackageContainer");
|
||||
|
||||
@@ -423,6 +470,8 @@ namespace YooAsset.Editor
|
||||
|
||||
private void RefreshWindow()
|
||||
{
|
||||
_highlightAssetPath = null;
|
||||
_highlightCollectorIndex = -1;
|
||||
_groupContainer.visible = false;
|
||||
_collectorContainer.visible = false;
|
||||
|
||||
@@ -455,6 +504,54 @@ namespace YooAsset.Editor
|
||||
{
|
||||
BundleCollectorSettingData.SaveFile();
|
||||
}
|
||||
private void OnSearchButtonClicked()
|
||||
{
|
||||
_highlightAssetPath = null;
|
||||
_highlightCollectorIndex = -1;
|
||||
FillCollectorViewData();
|
||||
|
||||
string searchInput = GetSearchInput();
|
||||
var pathError = CollectAssetSearchUtility.ValidateSearchPath(searchInput);
|
||||
if (pathError != ECollectAssetSearchError.None)
|
||||
{
|
||||
string message = CollectAssetSearchUtility.GetSearchPathErrorMessage(pathError, searchInput);
|
||||
ShowSearchResult(message, new Color(1f, 0.4f, 0.4f));
|
||||
return;
|
||||
}
|
||||
|
||||
var selectPackage = _packageListView.selectedItem as BundleCollectorPackage;
|
||||
if (selectPackage == null)
|
||||
{
|
||||
string message = "No package selected. Please select a package first.";
|
||||
ShowSearchResult(message, new Color(1f, 0.8f, 0.3f));
|
||||
return;
|
||||
}
|
||||
|
||||
var searchResult = CollectAssetSearchUtility.SearchAssetPath(selectPackage, searchInput);
|
||||
if (searchResult == null)
|
||||
{
|
||||
string message = $"No results found in package '{selectPackage.PackageName}'.";
|
||||
ShowSearchResult(message, new Color(1f, 0.8f, 0.3f));
|
||||
return;
|
||||
}
|
||||
|
||||
string resultMessage = $"Found in group '{searchResult.Group.GroupName}', collector '{searchResult.Collector.CollectPath}'";
|
||||
ShowSearchResult(resultMessage, Color.white);
|
||||
|
||||
_searchResultLabel.style.unityFontStyleAndWeight = FontStyle.Bold;
|
||||
_searchResultLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
|
||||
|
||||
_highlightAssetPath = searchResult.AssetPath;
|
||||
_highlightCollectorIndex = searchResult.CollectorIndex;
|
||||
|
||||
_groupContainer.visible = true;
|
||||
_lastModifyGroupIndex = searchResult.GroupIndex;
|
||||
|
||||
if (_groupListView.selectedIndex == searchResult.GroupIndex)
|
||||
FillCollectorViewData();
|
||||
else
|
||||
_groupListView.selectedIndex = searchResult.GroupIndex;
|
||||
}
|
||||
private void OnGlobalSettingsButtonClicked()
|
||||
{
|
||||
_showGlobalSettings = !_showGlobalSettings;
|
||||
@@ -480,6 +577,45 @@ namespace YooAsset.Editor
|
||||
return ruleDisplayName.ClassName;
|
||||
}
|
||||
|
||||
// 搜索栏相关
|
||||
private void ShowSearchResult(string message, Color color)
|
||||
{
|
||||
_searchResultLabel.text = message;
|
||||
_searchResultLabel.style.color = color;
|
||||
_searchResultLabel.style.unityFontStyleAndWeight = FontStyle.Normal;
|
||||
_searchResultLabel.style.unityTextAlign = TextAnchor.MiddleCenter;
|
||||
_searchResultLabel.style.display = DisplayStyle.Flex;
|
||||
}
|
||||
private void ClearSearchResult()
|
||||
{
|
||||
_searchResultLabel.text = string.Empty;
|
||||
_searchResultLabel.style.display = DisplayStyle.None;
|
||||
}
|
||||
private void ApplySearchPlaceholder()
|
||||
{
|
||||
_searchField.value = "Drag or enter asset path here (e.g. Assets/Res/icon.png)";
|
||||
if (_searchTextField.ClassListContains(PlaceholderClass) == false)
|
||||
_searchTextField.AddToClassList(PlaceholderClass);
|
||||
|
||||
var inputElement = _searchTextField.Q("unity-text-input");
|
||||
inputElement.style.color = new Color(0.7f, 0.7f, 0.7f, 0.6f);
|
||||
}
|
||||
private void ClearSearchPlaceholder()
|
||||
{
|
||||
if (_searchTextField.ClassListContains(PlaceholderClass))
|
||||
{
|
||||
_searchTextField.RemoveFromClassList(PlaceholderClass);
|
||||
var inputElement = _searchTextField.Q("unity-text-input");
|
||||
inputElement.style.color = StyleKeyword.Null;
|
||||
}
|
||||
}
|
||||
private string GetSearchInput()
|
||||
{
|
||||
if (_searchTextField.ClassListContains(PlaceholderClass))
|
||||
return string.Empty;
|
||||
return _searchField.value;
|
||||
}
|
||||
|
||||
// 设置栏相关
|
||||
private void RefreshSettings()
|
||||
{
|
||||
@@ -607,6 +743,8 @@ namespace YooAsset.Editor
|
||||
}
|
||||
private void OnPackageListViewSelectionChange(IEnumerable<object> objs)
|
||||
{
|
||||
ClearSearchResult();
|
||||
|
||||
var selectPackage = _packageListView.selectedItem as BundleCollectorPackage;
|
||||
if (selectPackage == null)
|
||||
{
|
||||
@@ -752,6 +890,15 @@ namespace YooAsset.Editor
|
||||
BindCollectorListViewItem(element, i);
|
||||
_collectorScrollView.Add(element);
|
||||
}
|
||||
|
||||
if (_highlightCollectorIndex >= 0 && _highlightCollectorIndex < selectGroup.Collectors.Count)
|
||||
{
|
||||
var targetElement = _collectorScrollView[_highlightCollectorIndex];
|
||||
var foldout = targetElement.Q<Foldout>("Foldout1");
|
||||
if (foldout != null)
|
||||
foldout.value = true;
|
||||
_highlightCollectorIndex = -1;
|
||||
}
|
||||
}
|
||||
private VisualElement MakeCollectorListViewItem()
|
||||
{
|
||||
@@ -1033,13 +1180,6 @@ namespace YooAsset.Editor
|
||||
// 清空旧元素
|
||||
foldout.Clear();
|
||||
|
||||
// 检测配置是否有效
|
||||
if (collector.IsValid() == false)
|
||||
{
|
||||
collector.CheckConfigError();
|
||||
return;
|
||||
}
|
||||
|
||||
List<CollectAssetInfo> collectAssetInfos = null;
|
||||
|
||||
try
|
||||
@@ -1055,12 +1195,15 @@ namespace YooAsset.Editor
|
||||
command.IncludeAssetGUID = _includeAssetGUIDToggle.value;
|
||||
command.AutoCollectShaders = _autoCollectShadersToggle.value;
|
||||
|
||||
// 检测配置是否有效
|
||||
collector.CheckConfigError();
|
||||
|
||||
// 收集有效资源信息
|
||||
collectAssetInfos = collector.GetAllCollectAssets(command, group);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError(e.ToString());
|
||||
Debug.LogError($"Invalid collector : {collector.CollectPath}, error: {e.Message}");
|
||||
}
|
||||
|
||||
if (collectAssetInfos != null)
|
||||
@@ -1085,6 +1228,13 @@ namespace YooAsset.Editor
|
||||
label.style.width = 300;
|
||||
label.style.marginLeft = 0;
|
||||
label.style.flexGrow = 1;
|
||||
|
||||
if (string.IsNullOrEmpty(_highlightAssetPath) == false &&
|
||||
string.Equals(collectAsset.AssetInfo.AssetPath, _highlightAssetPath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
label.style.color = new Color(1f, 0.2f, 0.2f);
|
||||
}
|
||||
|
||||
elementRow.Add(label);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
<ui:Button text="Import" display-tooltip-when-elided="true" name="ImportButton" style="width: 50px; background-color: rgb(56, 147, 58);" />
|
||||
<ui:Button text="Fix" display-tooltip-when-elided="true" name="FixButton" style="width: 50px; background-color: rgb(56, 147, 58);" />
|
||||
</uie:Toolbar>
|
||||
<uie:Toolbar name="SearchToolbar" style="display: flex; flex-direction: row;">
|
||||
<uie:ToolbarSearchField focusable="true" name="SearchField" style="flex-grow: 1;" />
|
||||
<ui:Button text="Search" display-tooltip-when-elided="true" name="SearchButton" style="width: 60px; background-color: rgb(56, 147, 58);" />
|
||||
</uie:Toolbar>
|
||||
<ui:Label name="SearchResultLabel" style="display: none; height: 24px; -unity-text-align: middle-center; padding-left: 5px; padding-right: 5px;" />
|
||||
<ui:VisualElement name="PublicContainer" style="background-color: rgb(79, 79, 79); border-left-width: 5px; border-right-width: 5px; border-top-width: 5px; border-bottom-width: 5px;">
|
||||
<ui:VisualElement name="HelpBoxContainer" style="flex-grow: 1;" />
|
||||
<ui:VisualElement name="GlobalSettingsContainer">
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 淘汰策略执行结果
|
||||
/// </summary>
|
||||
internal readonly struct EvictionResult
|
||||
{
|
||||
private readonly bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public string Error { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 需要清理的资源标识符集合
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> BundleGuids { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否执行成功
|
||||
/// </summary>
|
||||
public bool Succeeded
|
||||
{
|
||||
get { return _initialized && Error == null; }
|
||||
}
|
||||
|
||||
private EvictionResult(string error, IReadOnlyList<string> bundleGuids)
|
||||
{
|
||||
_initialized = true;
|
||||
Error = error;
|
||||
BundleGuids = bundleGuids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建表示执行成功的淘汰结果
|
||||
/// </summary>
|
||||
/// <param name="bundleGuids">需要清理的资源包标识符列表</param>
|
||||
/// <returns>携带待清理列表的成功结果</returns>
|
||||
public static EvictionResult CreateSuccess(IReadOnlyList<string> bundleGuids)
|
||||
{
|
||||
return new EvictionResult(null, bundleGuids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建表示执行失败的淘汰结果
|
||||
/// </summary>
|
||||
/// <param name="error">描述失败原因的错误信息</param>
|
||||
/// <returns>携带错误信息的失败结果</returns>
|
||||
public static EvictionResult CreateFailure(string error)
|
||||
{
|
||||
return new EvictionResult(error, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,5 +73,12 @@ namespace YooAsset
|
||||
/// <param name="bundleGuid">资源包的唯一标识符</param>
|
||||
/// <returns>如果缓存中存在该资源包则返回 true,否则返回 false。</returns>
|
||||
bool IsCached(string bundleGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 获取已缓存的资源包文件路径
|
||||
/// </summary>
|
||||
/// <param name="bundleGuid">资源包的唯一标识符</param>
|
||||
/// <returns>返回已缓存的资源包文件路径,如果不存在则返回 null。</returns>
|
||||
string GetCacheFilePath(string bundleGuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace YooAsset
|
||||
internal interface ICacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Bundle 唯一标识
|
||||
/// 资源包唯一标识
|
||||
/// </summary>
|
||||
string BundleGuid { get; }
|
||||
}
|
||||
|
||||
@@ -2,6 +2,59 @@ using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 淘汰策略执行结果
|
||||
/// </summary>
|
||||
internal readonly struct EvictionResult
|
||||
{
|
||||
private readonly bool _initialized;
|
||||
|
||||
/// <summary>
|
||||
/// 错误信息
|
||||
/// </summary>
|
||||
public string Error { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 需要清理的资源标识符集合
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> BundleGuids { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否执行成功
|
||||
/// </summary>
|
||||
public bool Succeeded
|
||||
{
|
||||
get { return _initialized && Error == null; }
|
||||
}
|
||||
|
||||
private EvictionResult(string error, IReadOnlyList<string> bundleGuids)
|
||||
{
|
||||
_initialized = true;
|
||||
Error = error;
|
||||
BundleGuids = bundleGuids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建表示执行成功的淘汰结果
|
||||
/// </summary>
|
||||
/// <param name="bundleGuids">需要清理的资源包标识符列表</param>
|
||||
/// <returns>携带待清理列表的成功结果</returns>
|
||||
public static EvictionResult CreateSuccess(IReadOnlyList<string> bundleGuids)
|
||||
{
|
||||
return new EvictionResult(null, bundleGuids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建表示执行失败的淘汰结果
|
||||
/// </summary>
|
||||
/// <param name="error">描述失败原因的错误信息</param>
|
||||
/// <returns>携带错误信息的失败结果</returns>
|
||||
public static EvictionResult CreateFailure(string error)
|
||||
{
|
||||
return new EvictionResult(error, null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存淘汰策略接口
|
||||
/// </summary>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 清理缓存操作选项
|
||||
/// 清理缓存的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct BCClearCacheOptions
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载资源包操作选项
|
||||
/// 加载资源包的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct BCLoadBundleOptions
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 验证缓存操作选项
|
||||
/// 验证缓存的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct BCVerifyCacheOptions
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 写入缓存操作选项
|
||||
/// 写入缓存的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct BCWriteCacheOptions
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 创建加载内置资源目录操作实例
|
||||
/// </summary>
|
||||
/// <param name="options">加载内置资源目录的配置选项</param>
|
||||
/// <param name="options">加载内置资源目录的操作选项</param>
|
||||
internal LoadBuiltinCatalogOperation(LoadBuiltinCatalogOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载内置资源目录操作选项
|
||||
/// 加载内置资源目录的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadBuiltinCatalogOptions
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 创建本地 ArchiveBundle 加载操作实例
|
||||
/// </summary>
|
||||
/// <param name="options">从本地加载 ArchiveBundle 的配置选项</param>
|
||||
/// <param name="options">从本地加载 ArchiveBundle 的操作选项</param>
|
||||
public LoadLocalArchiveBundleOperation(LoadLocalArchiveBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
@@ -38,26 +38,51 @@ namespace YooAsset
|
||||
|
||||
if (_steps == ESteps.LoadBundle)
|
||||
{
|
||||
if (_options.Bundle.IsEncrypted)
|
||||
if (_options.Bundle.IsEncrypted == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"ArchiveBundle encrypted loading is not supported: '{_options.FilePath}'.");
|
||||
return;
|
||||
}
|
||||
if (FileUtility.IsFileIOSupported(_options.FilePath) == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"FileIO is not supported for builtin path: '{_options.FilePath}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (FileUtility.IsFileIOSupported(_options.FilePath) == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"FileIO is not supported for builtin path: '{_options.FilePath}'.");
|
||||
return;
|
||||
LoadResult result = LoadFromFile();
|
||||
if (result.Succeeded == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(result.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LoadResult result = ParseArchiveFile();
|
||||
if (result.Succeeded == false)
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(result.Error);
|
||||
return;
|
||||
var decryptor = _options.ArchiveBundleDecryptor;
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} archive bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadResult result;
|
||||
if (decryptor is IBundleMemoryDecryptor memoryDecryptor)
|
||||
{
|
||||
result = LoadFromMemory(memoryDecryptor);
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for ArchiveBundle.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.Succeeded == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(result.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_steps = ESteps.CheckResult;
|
||||
@@ -74,7 +99,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new ArchiveBundleHandle(_options.FilePath, _options.Bundle, _archiveBundle);
|
||||
BundleHandle = new ArchiveBundleHandle(_options.Bundle, _archiveBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,16 +108,33 @@ namespace YooAsset
|
||||
ExecuteBatch();
|
||||
}
|
||||
|
||||
private LoadResult ParseArchiveFile()
|
||||
private LoadResult LoadFromFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
_archiveBundle = ArchiveBundleHelper.LoadArchiveBundle(_options.FilePath);
|
||||
_archiveBundle = ArchiveBundleHelper.LoadFromFile(_options.FilePath);
|
||||
return LoadResult.Default();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LoadResult.Failure($"Failed to parse archive file: {ex.Message}.");
|
||||
return LoadResult.Failure($"Failed to load archive bundle file: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
|
||||
{
|
||||
try
|
||||
{
|
||||
var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
|
||||
byte[] binaryData = decryptor.GetDecryptedData(args);
|
||||
if (binaryData == null)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
|
||||
|
||||
_archiveBundle = ArchiveBundleHelper.LoadFromMemory(binaryData);
|
||||
return LoadResult.Default();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LoadResult.Failure($"Failed to load archive bundle file from memory: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载 ArchiveBundle 的上下文信息
|
||||
/// 本地加载 ArchiveBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadLocalArchiveBundleOptions
|
||||
{
|
||||
@@ -21,11 +21,17 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public string FilePath { get; }
|
||||
|
||||
public LoadLocalArchiveBundleOptions(string cacheName, PackageBundle bundle, string filePath)
|
||||
/// <summary>
|
||||
/// ArchiveBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor ArchiveBundleDecryptor { get; }
|
||||
|
||||
public LoadLocalArchiveBundleOptions(string cacheName, PackageBundle bundle, string filePath, IBundleDecryptor archiveBundleDecryptor)
|
||||
{
|
||||
CacheName = cacheName;
|
||||
Bundle = bundle;
|
||||
FilePath = filePath;
|
||||
ArchiveBundleDecryptor = archiveBundleDecryptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 创建本地 AssetBundle 加载操作实例
|
||||
/// </summary>
|
||||
/// <param name="options">从本地加载 AssetBundle 的配置选项</param>
|
||||
/// <param name="options">从本地加载 AssetBundle 的操作选项</param>
|
||||
public LoadLocalAssetBundleOperation(LoadLocalAssetBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
@@ -56,14 +56,14 @@ namespace YooAsset
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} decryptor is null.");
|
||||
SetError($"{_options.CacheName} asset bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadResult result;
|
||||
if (decryptor is IBundleOffsetDecryptor offsetDecryptor)
|
||||
{
|
||||
result = LoadFromFileWithOffset(offsetDecryptor);
|
||||
result = LoadFromFile(offsetDecryptor);
|
||||
}
|
||||
else if (decryptor is IBundleMemoryDecryptor memoryDecryptor)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace YooAsset
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for AssetBundle.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new AssetBundleHandle(_options.FilePath, _options.Bundle, _assetBundle, _loadStream);
|
||||
BundleHandle = new AssetBundleHandle(_options.Bundle, _assetBundle, _loadStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,18 +137,17 @@ namespace YooAsset
|
||||
else
|
||||
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath);
|
||||
}
|
||||
private LoadResult LoadFromFileWithOffset(IBundleOffsetDecryptor decryptor)
|
||||
private LoadResult LoadFromFile(IBundleOffsetDecryptor decryptor)
|
||||
{
|
||||
var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
|
||||
long rawOffset = decryptor.GetFileOffset(args);
|
||||
if (rawOffset < 0)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned negative offset: {rawOffset}.");
|
||||
ulong offset = (ulong)rawOffset;
|
||||
long offset = decryptor.GetFileOffset(args);
|
||||
if (offset < 0)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned negative offset: {offset}.");
|
||||
|
||||
if (IsWaitForCompletion)
|
||||
_assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, offset);
|
||||
_assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, (ulong)offset);
|
||||
else
|
||||
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath, 0, offset);
|
||||
_createRequest = AssetBundle.LoadFromFileAsync(_options.FilePath, 0, (ulong)offset);
|
||||
|
||||
return LoadResult.Default();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载 AssetBundle 的上下文信息
|
||||
/// 本地加载 AssetBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadLocalAssetBundleOptions
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace YooAsset
|
||||
/// <summary>
|
||||
/// 创建本地 RawBundle 加载操作实例
|
||||
/// </summary>
|
||||
/// <param name="options">从本地加载 RawBundle 的配置选项</param>
|
||||
/// <param name="options">从本地加载 RawBundle 的操作选项</param>
|
||||
public LoadLocalRawBundleOperation(LoadLocalRawBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
@@ -62,7 +61,7 @@ namespace YooAsset
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} decryptor is null.");
|
||||
SetError($"{_options.CacheName} raw bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +73,7 @@ namespace YooAsset
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for RawBundle.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,7 +99,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new RawBundleHandle(_options.FilePath, _options.Bundle, _rawBundle);
|
||||
BundleHandle = new RawBundleHandle(_options.Bundle, _rawBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,13 +112,12 @@ namespace YooAsset
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(_options.FilePath);
|
||||
_rawBundle = new RawBundle(data);
|
||||
_rawBundle = RawBundleHelper.LoadFromFile(_options.FilePath);
|
||||
return LoadResult.Default();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LoadResult.Failure($"Failed to read raw bundle file: {ex.Message}.");
|
||||
return LoadResult.Failure($"Failed to load raw bundle file: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
|
||||
@@ -129,7 +127,7 @@ namespace YooAsset
|
||||
if (binaryData == null)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
|
||||
|
||||
_rawBundle = new RawBundle(binaryData);
|
||||
_rawBundle = RawBundleHelper.LoadFromMemory(binaryData);
|
||||
return LoadResult.Default();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载 RawBundle 的上下文信息
|
||||
/// 本地加载 RawBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadLocalRawBundleOptions
|
||||
{
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 平台加载 ArchiveBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class LoadWebArchiveBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
Prepare,
|
||||
DataRequest,
|
||||
CheckRequest,
|
||||
VerifyData,
|
||||
LoadBundle,
|
||||
TryAgain,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly LoadWebArchiveBundleOptions _options;
|
||||
private readonly DownloadRetryController _downloadRetryController;
|
||||
private IDownloadBytesRequest _downloadBytesRequest;
|
||||
private IBundleMemoryDecryptor _decryptor;
|
||||
private ArchiveBundle _archiveBundle;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal LoadWebArchiveBundleOperation(LoadWebArchiveBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
// 注意:网络原因失败后,重新尝试直到成功
|
||||
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.Prepare;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.Prepare)
|
||||
{
|
||||
if (_options.Bundle.IsEncrypted == false)
|
||||
{
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
var decryptor = _options.ArchiveBundleDecryptor;
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} archive bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (decryptor is IBundleMemoryDecryptor)
|
||||
{
|
||||
_decryptor = decryptor as IBundleMemoryDecryptor;
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for ArchiveBundle.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.DataRequest)
|
||||
{
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadDataRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout);
|
||||
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
|
||||
_downloadBytesRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.CheckRequest)
|
||||
{
|
||||
Progress = _downloadBytesRequest.DownloadProgress;
|
||||
if (_downloadBytesRequest.IsDone == false)
|
||||
return;
|
||||
|
||||
if (_downloadBytesRequest.Status == EDownloadRequestStatus.Succeeded)
|
||||
{
|
||||
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadBytesRequest.Url);
|
||||
_steps = ESteps.VerifyData;
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = _downloadBytesRequest.Url;
|
||||
long httpCode = _downloadBytesRequest.HttpCode;
|
||||
string httpError = _downloadBytesRequest.HttpError;
|
||||
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
|
||||
{
|
||||
_downloadRetryController.StartRetryDelay();
|
||||
_steps = ESteps.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_downloadBytesRequest.Error);
|
||||
YooLogger.LogError(Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.VerifyData)
|
||||
{
|
||||
// 注意:网络/代理/服务器异常导致内容不完整但请求仍成功
|
||||
EFileVerifyResult verifyResult;
|
||||
if (_options.DownloadVerifyLevel == EFileVerifyLevel.Low || _options.DownloadVerifyLevel == EFileVerifyLevel.Middle)
|
||||
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, 0);
|
||||
else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High)
|
||||
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc);
|
||||
else
|
||||
throw new YooInternalException($"Unexpected verify level: {_options.DownloadVerifyLevel}.");
|
||||
|
||||
if (verifyResult == EFileVerifyResult.Succeed)
|
||||
{
|
||||
_steps = ESteps.LoadBundle;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
|
||||
YooLogger.LogWarning(error);
|
||||
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
|
||||
{
|
||||
_downloadRetryController.StartRetryDelay();
|
||||
_steps = ESteps.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.LoadBundle)
|
||||
{
|
||||
LoadResult result = LoadFromMemory(_decryptor, _downloadBytesRequest.Result);
|
||||
if (result.Succeeded == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(result.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new ArchiveBundleHandle(_options.Bundle, _archiveBundle);
|
||||
}
|
||||
|
||||
if (_steps == ESteps.TryAgain)
|
||||
{
|
||||
// 注意:失败后释放网络请求
|
||||
if (_downloadBytesRequest != null)
|
||||
{
|
||||
_downloadBytesRequest.Dispose();
|
||||
_downloadBytesRequest = null;
|
||||
}
|
||||
|
||||
if (_downloadRetryController.TickRetryDelay())
|
||||
{
|
||||
Progress = 0f;
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
if (_downloadBytesRequest != null)
|
||||
{
|
||||
_downloadBytesRequest.Dispose();
|
||||
_downloadBytesRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decryptor != null)
|
||||
{
|
||||
var args = new BundleDecryptArgs(_options.Bundle, fileData, null);
|
||||
var binaryData = decryptor.GetDecryptedData(args);
|
||||
if (binaryData == null)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
|
||||
|
||||
_archiveBundle = ArchiveBundleHelper.LoadFromMemory(binaryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_archiveBundle = ArchiveBundleHelper.LoadFromMemory(fileData);
|
||||
}
|
||||
return LoadResult.Default();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LoadResult.Failure($"Failed to load archive bundle from memory: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f0fd9af541471154d9fc968abd450d31
|
||||
guid: 00ec926bdc7c65e40a464570d27570d9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 平台加载 ArchiveBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadWebArchiveBundleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件缓存名称
|
||||
/// </summary>
|
||||
public string CacheName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包描述
|
||||
/// </summary>
|
||||
public PackageBundle Bundle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 候选下载地址列表
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> CandidateUrls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ArchiveBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor ArchiveBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载后台接口
|
||||
/// </summary>
|
||||
public IDownloadBackend DownloadBackend { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载数据校验级别
|
||||
/// </summary>
|
||||
public EFileVerifyLevel DownloadVerifyLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 看门狗超时时间
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载重试判定策略
|
||||
/// </summary>
|
||||
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// URL 选择策略
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public LoadWebArchiveBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
IBundleDecryptor archiveBundleDecryptor, IDownloadBackend downloadBackend, EFileVerifyLevel downloadVerifyLevel,
|
||||
int watchdogTimeout, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
CacheName = cacheName;
|
||||
Bundle = bundle;
|
||||
CandidateUrls = candidateUrls;
|
||||
ArchiveBundleDecryptor = archiveBundleDecryptor;
|
||||
DownloadBackend = downloadBackend;
|
||||
DownloadVerifyLevel = downloadVerifyLevel;
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
DownloadUrlPolicy = downloadUrlPolicy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87ff97cf646c9e240a2f2f2e37963353
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,136 +3,14 @@ using UnityEngine;
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 从网络加载未加密 AssetBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class LoadWebNormalAssetBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
BundleRequest,
|
||||
CheckRequest,
|
||||
TryAgain,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly LoadWebAssetBundleOptions _options;
|
||||
private readonly DownloadRetryController _downloadRetryController;
|
||||
private IDownloadAssetBundleRequest _downloadAssetBundleRequest;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
/// <summary>
|
||||
/// 创建 LoadWebNormalAssetBundleOperation 实例
|
||||
/// </summary>
|
||||
/// <param name="options">从网络加载 AssetBundle 的配置选项</param>
|
||||
public LoadWebNormalAssetBundleOperation(LoadWebAssetBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
// 注意:网络原因失败后,重新尝试直到成功
|
||||
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.BundleRequest;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.BundleRequest)
|
||||
{
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadAssetBundleRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout,
|
||||
disableUnityWebCache: _options.DisableUnityWebCache,
|
||||
fileHash: _options.Bundle.FileHash,
|
||||
unityCrc: _options.Bundle.UnityCrc);
|
||||
_downloadAssetBundleRequest = _options.DownloadBackend.CreateAssetBundleRequest(args);
|
||||
_downloadAssetBundleRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.CheckRequest)
|
||||
{
|
||||
Progress = _downloadAssetBundleRequest.DownloadProgress;
|
||||
if (_downloadAssetBundleRequest.IsDone == false)
|
||||
return;
|
||||
|
||||
if (_downloadAssetBundleRequest.Status == EDownloadRequestStatus.Succeeded)
|
||||
{
|
||||
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadAssetBundleRequest.Url);
|
||||
var assetBundle = _downloadAssetBundleRequest.Result;
|
||||
if (assetBundle == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"Downloaded asset bundle is null. URL: {_downloadAssetBundleRequest.Url}");
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new AssetBundleHandle(_downloadAssetBundleRequest.Url, _options.Bundle, assetBundle, null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = _downloadAssetBundleRequest.Url;
|
||||
long httpCode = _downloadAssetBundleRequest.HttpCode;
|
||||
string httpError = _downloadAssetBundleRequest.HttpError;
|
||||
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
|
||||
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
|
||||
{
|
||||
_downloadRetryController.StartRetryDelay();
|
||||
_steps = ESteps.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_downloadAssetBundleRequest.Error);
|
||||
YooLogger.LogError(Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.TryAgain)
|
||||
{
|
||||
// 注意:失败后释放网络请求
|
||||
if (_downloadAssetBundleRequest != null)
|
||||
{
|
||||
_downloadAssetBundleRequest.Dispose();
|
||||
_downloadAssetBundleRequest = null;
|
||||
}
|
||||
|
||||
if (_downloadRetryController.TickRetryDelay())
|
||||
{
|
||||
Progress = 0f;
|
||||
_steps = ESteps.BundleRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
if (_downloadAssetBundleRequest != null)
|
||||
{
|
||||
_downloadAssetBundleRequest.Dispose();
|
||||
_downloadAssetBundleRequest = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从网络加载加密的 AssetBundle 操作
|
||||
/// WebGL 平台加载加密 AssetBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class LoadWebEncryptedAssetBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
Prepare,
|
||||
DataRequest,
|
||||
CheckRequest,
|
||||
VerifyData,
|
||||
@@ -142,18 +20,14 @@ namespace YooAsset
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly LoadWebAssetBundleOptions _options;
|
||||
private readonly LoadWebEncryptedAssetBundleOptions _options;
|
||||
private readonly DownloadRetryController _downloadRetryController;
|
||||
private IDownloadBytesRequest _downloadBytesRequest;
|
||||
private IBundleMemoryDecryptor _decryptor;
|
||||
private AssetBundleCreateRequest _createRequest;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
/// <summary>
|
||||
/// 创建 LoadWebEncryptedAssetBundleOperation 实例
|
||||
/// </summary>
|
||||
/// <param name="options">从网络加载 AssetBundle 的配置选项</param>
|
||||
public LoadWebEncryptedAssetBundleOperation(LoadWebAssetBundleOptions options)
|
||||
public LoadWebEncryptedAssetBundleOperation(LoadWebEncryptedAssetBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
@@ -162,43 +36,48 @@ namespace YooAsset
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.DataRequest;
|
||||
_steps = ESteps.Prepare;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.DataRequest)
|
||||
if (_steps == ESteps.Prepare)
|
||||
{
|
||||
var decryptor = _options.AssetBundleDecryptor;
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} decryptor is null.");
|
||||
SetError($"{_options.CacheName} asset bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (decryptor is IBundleMemoryDecryptor)
|
||||
{
|
||||
_decryptor = decryptor as IBundleMemoryDecryptor;
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadDataRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout);
|
||||
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
|
||||
_downloadBytesRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}'.");
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for ArchiveBundle.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.DataRequest)
|
||||
{
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadDataRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout);
|
||||
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
|
||||
_downloadBytesRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.CheckRequest)
|
||||
{
|
||||
Progress = _downloadBytesRequest.DownloadProgress;
|
||||
@@ -225,6 +104,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_downloadBytesRequest.Error);
|
||||
YooLogger.LogError(Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,7 +126,7 @@ namespace YooAsset
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"[WebBundleVerify] Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
|
||||
string error = $"Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
|
||||
YooLogger.LogWarning(error);
|
||||
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
|
||||
@@ -284,13 +164,13 @@ namespace YooAsset
|
||||
if (assetBundle == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError("Unity engine load failed.");
|
||||
SetError("Unity engine load asset bundle failed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new AssetBundleHandle(_downloadBytesRequest.Url, _options.Bundle, assetBundle, null);
|
||||
BundleHandle = new AssetBundleHandle(_options.Bundle, assetBundle, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 559636cb5e3e48e4bb79e14a70a1ea4f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载 AssetBundle 的上下文信息
|
||||
/// WebGL 平台加载加密 AssetBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadWebAssetBundleOptions
|
||||
internal readonly struct LoadWebEncryptedAssetBundleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件缓存名称
|
||||
@@ -42,11 +42,6 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 禁用 Unity 内置网络缓存
|
||||
/// </summary>
|
||||
public bool DisableUnityWebCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载重试判定策略
|
||||
/// </summary>
|
||||
@@ -57,9 +52,9 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public LoadWebAssetBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
public LoadWebEncryptedAssetBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
IBundleDecryptor assetBundleDecryptor, IDownloadBackend downloadBackend, EFileVerifyLevel downloadVerifyLevel,
|
||||
int watchdogTimeout, bool disableUnityWebCache, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
int watchdogTimeout, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
CacheName = cacheName;
|
||||
Bundle = bundle;
|
||||
@@ -68,7 +63,6 @@ namespace YooAsset
|
||||
DownloadBackend = downloadBackend;
|
||||
DownloadVerifyLevel = downloadVerifyLevel;
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DisableUnityWebCache = disableUnityWebCache;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
DownloadUrlPolicy = downloadUrlPolicy;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e98882b2de6b0644fab7549b1a19eb76
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,11 +1,9 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 从 WebGL 游戏平台加载非加密 AssetBundle 操作
|
||||
/// WebGL 平台加载非加密 AssetBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class LoadWebGameAssetBundleOperation : BCLoadBundleOperation
|
||||
internal sealed class LoadWebPlatformAssetBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
@@ -16,12 +14,12 @@ namespace YooAsset
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly LoadWebGameAssetBundleOptions _options;
|
||||
private readonly LoadWebPlatformAssetBundleOptions _options;
|
||||
private readonly DownloadRetryController _downloadRetryController;
|
||||
private IDownloadAssetBundleRequest _downloadAssetBundleRequest;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal LoadWebGameAssetBundleOperation(LoadWebGameAssetBundleOptions options)
|
||||
internal LoadWebPlatformAssetBundleOperation(LoadWebPlatformAssetBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
@@ -40,11 +38,15 @@ namespace YooAsset
|
||||
if (_steps == ESteps.BundleRequest)
|
||||
{
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadRequestArgs(
|
||||
var args = new DownloadAssetBundleRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout);
|
||||
_downloadAssetBundleRequest = new WebGameAssetBundleRequest(args, _options.GamePlatform);
|
||||
watchdogTimeout: _options.WatchdogTimeout,
|
||||
disableUnityWebCache: _options.DisableUnityWebCache,
|
||||
fileHash: _options.Bundle.FileHash,
|
||||
unityCrc: _options.Bundle.UnityCrc,
|
||||
platformStrategy: _options.PlatformStrategy);
|
||||
_downloadAssetBundleRequest = _options.DownloadBackend.CreateAssetBundleRequest(args);
|
||||
_downloadAssetBundleRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
}
|
||||
@@ -69,7 +71,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new WebGameAssetBundleHandle(_options.CacheFilePath, _options.Bundle, assetBundle, _options.GamePlatform);
|
||||
BundleHandle = new WebAssetBundleHandle(_options.Bundle, assetBundle, _options.PlatformStrategy);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc432c9fb60357e4f985284caae506f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 游戏平台加载 AssetBundle 的配置选项
|
||||
/// WebGL 平台加载非加密 AssetBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadWebGameAssetBundleOptions
|
||||
internal readonly struct LoadWebPlatformAssetBundleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包描述
|
||||
@@ -18,20 +18,25 @@ namespace YooAsset
|
||||
public IReadOnlyList<string> CandidateUrls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 游戏平台接口
|
||||
/// 平台策略
|
||||
/// </summary>
|
||||
public IWebGamePlatform GamePlatform { get; }
|
||||
public IWebPlatformStrategy PlatformStrategy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台侧缓存文件路径
|
||||
/// 下载后台接口
|
||||
/// </summary>
|
||||
public string CacheFilePath { get; }
|
||||
public IDownloadBackend DownloadBackend { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 看门狗超时时间
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 禁用 Unity 内置网络缓存
|
||||
/// </summary>
|
||||
public bool DisableUnityWebCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载重试判定策略
|
||||
/// </summary>
|
||||
@@ -42,15 +47,16 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public LoadWebGameAssetBundleOptions(PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
IWebGamePlatform gamePlatform, string cacheFilePath, int watchdogTimeout,
|
||||
public LoadWebPlatformAssetBundleOptions(PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
IWebPlatformStrategy platformStrategy, IDownloadBackend downloadBackend, int watchdogTimeout, bool disableUnityWebCache,
|
||||
IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
Bundle = bundle;
|
||||
CandidateUrls = candidateUrls;
|
||||
GamePlatform = gamePlatform;
|
||||
CacheFilePath = cacheFilePath;
|
||||
PlatformStrategy = platformStrategy;
|
||||
DownloadBackend = downloadBackend;
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DisableUnityWebCache = disableUnityWebCache;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
DownloadUrlPolicy = downloadUrlPolicy;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28cb5372ae5766e409aab15dfe109e95
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 平台加载 RawBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class LoadWebRawBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
Prepare,
|
||||
DataRequest,
|
||||
CheckRequest,
|
||||
VerifyData,
|
||||
LoadBundle,
|
||||
TryAgain,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly LoadWebRawBundleOptions _options;
|
||||
private readonly DownloadRetryController _downloadRetryController;
|
||||
private IDownloadBytesRequest _downloadBytesRequest;
|
||||
private IBundleMemoryDecryptor _decryptor;
|
||||
private RawBundle _rawBundle;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal LoadWebRawBundleOperation(LoadWebRawBundleOptions options)
|
||||
{
|
||||
_options = options;
|
||||
|
||||
// 注意:网络原因失败后,重新尝试直到成功
|
||||
_downloadRetryController = new DownloadRetryController(int.MaxValue, options.DownloadRetryPolicy);
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.Prepare;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.Prepare)
|
||||
{
|
||||
if (_options.Bundle.IsEncrypted == false)
|
||||
{
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
var decryptor = _options.RawBundleDecryptor;
|
||||
if (decryptor == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} raw bundle decryptor is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (decryptor is IBundleMemoryDecryptor)
|
||||
{
|
||||
_decryptor = decryptor as IBundleMemoryDecryptor;
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"{_options.CacheName} does not support '{decryptor.GetType().Name}' for RawBundle.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.DataRequest)
|
||||
{
|
||||
string url = _options.DownloadUrlPolicy.SelectUrl(_options.CandidateUrls);
|
||||
var args = new DownloadDataRequestArgs(
|
||||
url: url,
|
||||
timeout: 0,
|
||||
watchdogTimeout: _options.WatchdogTimeout);
|
||||
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
|
||||
_downloadBytesRequest.SendRequest();
|
||||
_steps = ESteps.CheckRequest;
|
||||
}
|
||||
|
||||
if (_steps == ESteps.CheckRequest)
|
||||
{
|
||||
Progress = _downloadBytesRequest.DownloadProgress;
|
||||
if (_downloadBytesRequest.IsDone == false)
|
||||
return;
|
||||
|
||||
if (_downloadBytesRequest.Status == EDownloadRequestStatus.Succeeded)
|
||||
{
|
||||
_options.DownloadUrlPolicy.OnRequestSucceeded(_downloadBytesRequest.Url);
|
||||
_steps = ESteps.VerifyData;
|
||||
}
|
||||
else
|
||||
{
|
||||
string url = _downloadBytesRequest.Url;
|
||||
long httpCode = _downloadBytesRequest.HttpCode;
|
||||
string httpError = _downloadBytesRequest.HttpError;
|
||||
_options.DownloadUrlPolicy.OnRequestFailed(url, httpCode, httpError);
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.CanRetryRequest(url, httpCode, httpError))
|
||||
{
|
||||
_downloadRetryController.StartRetryDelay();
|
||||
_steps = ESteps.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_downloadBytesRequest.Error);
|
||||
YooLogger.LogError(Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.VerifyData)
|
||||
{
|
||||
// 注意:网络/代理/服务器异常导致内容不完整但请求仍成功
|
||||
EFileVerifyResult verifyResult;
|
||||
if (_options.DownloadVerifyLevel == EFileVerifyLevel.Low || _options.DownloadVerifyLevel == EFileVerifyLevel.Middle)
|
||||
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, 0);
|
||||
else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High)
|
||||
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc);
|
||||
else
|
||||
throw new YooInternalException($"Unexpected verify level: {_options.DownloadVerifyLevel}.");
|
||||
|
||||
if (verifyResult == EFileVerifyResult.Succeed)
|
||||
{
|
||||
_steps = ESteps.LoadBundle;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
|
||||
YooLogger.LogWarning(error);
|
||||
|
||||
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
|
||||
{
|
||||
_downloadRetryController.StartRetryDelay();
|
||||
_steps = ESteps.TryAgain;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.LoadBundle)
|
||||
{
|
||||
LoadResult result = LoadFromMemory(_decryptor, _downloadBytesRequest.Result);
|
||||
if (result.Succeeded == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(result.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new RawBundleHandle(_options.Bundle, _rawBundle);
|
||||
}
|
||||
|
||||
if (_steps == ESteps.TryAgain)
|
||||
{
|
||||
// 注意:失败后释放网络请求
|
||||
if (_downloadBytesRequest != null)
|
||||
{
|
||||
_downloadBytesRequest.Dispose();
|
||||
_downloadBytesRequest = null;
|
||||
}
|
||||
|
||||
if (_downloadRetryController.TickRetryDelay())
|
||||
{
|
||||
Progress = 0f;
|
||||
_steps = ESteps.DataRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
if (_downloadBytesRequest != null)
|
||||
{
|
||||
_downloadBytesRequest.Dispose();
|
||||
_downloadBytesRequest = null;
|
||||
}
|
||||
}
|
||||
|
||||
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (decryptor != null)
|
||||
{
|
||||
var args = new BundleDecryptArgs(_options.Bundle, fileData, null);
|
||||
var binaryData = decryptor.GetDecryptedData(args);
|
||||
if (binaryData == null)
|
||||
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
|
||||
|
||||
_rawBundle = RawBundleHelper.LoadFromMemory(binaryData);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rawBundle = RawBundleHelper.LoadFromMemory(fileData);
|
||||
}
|
||||
return LoadResult.Default();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return LoadResult.Failure($"Failed to load archive bundle from memory: {ex.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b30e1d41b966e63488837244e907363b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,70 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 平台加载 RawBundle 的操作选项
|
||||
/// </summary>
|
||||
internal readonly struct LoadWebRawBundleOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件缓存名称
|
||||
/// </summary>
|
||||
public string CacheName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包描述
|
||||
/// </summary>
|
||||
public PackageBundle Bundle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 候选下载地址列表
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> CandidateUrls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// RawBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor RawBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载后台接口
|
||||
/// </summary>
|
||||
public IDownloadBackend DownloadBackend { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载数据校验级别
|
||||
/// </summary>
|
||||
public EFileVerifyLevel DownloadVerifyLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 看门狗超时时间
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载重试判定策略
|
||||
/// </summary>
|
||||
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// URL 选择策略
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public LoadWebRawBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls,
|
||||
IBundleDecryptor rawBundleDecryptor, IDownloadBackend downloadBackend, EFileVerifyLevel downloadVerifyLevel,
|
||||
int watchdogTimeout, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
CacheName = cacheName;
|
||||
Bundle = bundle;
|
||||
CandidateUrls = candidateUrls;
|
||||
RawBundleDecryptor = rawBundleDecryptor;
|
||||
DownloadBackend = downloadBackend;
|
||||
DownloadVerifyLevel = downloadVerifyLevel;
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
DownloadUrlPolicy = downloadUrlPolicy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3f863ac69271e243b9270f23c70b02e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -20,15 +20,22 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IBundleDecryptor RawBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ArchiveBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor ArchiveBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载后台
|
||||
/// </summary>
|
||||
public IDownloadBackend DownloadBackend { get; }
|
||||
|
||||
public Configuration(IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IDownloadBackend downloadBackend)
|
||||
public Configuration(IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
|
||||
IBundleDecryptor archiveBundleDecryptor, IDownloadBackend downloadBackend)
|
||||
{
|
||||
AssetBundleDecryptor = assetBundleDecryptor;
|
||||
RawBundleDecryptor = rawBundleDecryptor;
|
||||
ArchiveBundleDecryptor = archiveBundleDecryptor;
|
||||
DownloadBackend = downloadBackend;
|
||||
}
|
||||
}
|
||||
@@ -135,6 +142,17 @@ namespace YooAsset
|
||||
{
|
||||
return _cacheEntries.ContainsKey(bundleGuid);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public string GetCacheFilePath(string bundleGuid)
|
||||
{
|
||||
if (_cacheEntries.TryGetValue(bundleGuid, out BuiltinBundleCacheEntry entry))
|
||||
{
|
||||
return entry.FilePath;
|
||||
}
|
||||
|
||||
YooLogger.LogWarning($"Cache file path not found. Bundle GUID: '{bundleGuid}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
#region 内部方法
|
||||
/// <summary>
|
||||
|
||||
@@ -60,7 +60,8 @@ namespace YooAsset
|
||||
var options = new LoadLocalArchiveBundleOptions(
|
||||
cacheName: _fileCache.GetType().Name,
|
||||
bundle: _bundle,
|
||||
filePath: _cacheEntry.FilePath);
|
||||
filePath: _cacheEntry.FilePath,
|
||||
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor);
|
||||
_loadLocalArchiveBundleOp = new LoadLocalArchiveBundleOperation(options);
|
||||
_loadLocalArchiveBundleOp.StartOperation();
|
||||
AddChildOperation(_loadLocalArchiveBundleOp);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace YooAsset
|
||||
PackageName = packageName;
|
||||
RootPath = rootPath;
|
||||
Config = config;
|
||||
IsReadOnly = true;
|
||||
IsReadOnly = config.VirtualDownloadMode == false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -142,10 +142,16 @@ namespace YooAsset
|
||||
/// <inheritdoc />
|
||||
public bool IsCached(string bundleGuid)
|
||||
{
|
||||
if (Config.VirtualDownloadMode)
|
||||
return _cacheEntries.ContainsKey(bundleGuid);
|
||||
else
|
||||
if (Config.VirtualDownloadMode == false)
|
||||
return true;
|
||||
|
||||
return _cacheEntries.ContainsKey(bundleGuid);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public string GetCacheFilePath(string bundleGuid)
|
||||
{
|
||||
YooLogger.LogWarning($"{nameof(EditorBundleCache)} does not support local cache file path.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,8 +208,43 @@ namespace YooAsset
|
||||
if (_cacheEntries.TryGetValue(bundleGuid, out EditorBundleCacheEntry entry))
|
||||
{
|
||||
_cacheEntries.Remove(bundleGuid);
|
||||
entry.Delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 marker 文件路径
|
||||
/// </summary>
|
||||
/// <param name="bundle">资源包描述</param>
|
||||
/// <returns>marker 文件的完整路径</returns>
|
||||
internal string GetMarkerFilePath(PackageBundle bundle)
|
||||
{
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
string hashFolder = GetHashFolderName(bundleGuid);
|
||||
return PathUtility.Combine(RootPath, hashFolder, bundleGuid, EditorBundleCacheConsts.MarkerFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 marker 临时文件路径
|
||||
/// </summary>
|
||||
/// <param name="bundle">资源包描述</param>
|
||||
/// <returns>marker 临时文件的完整路径</returns>
|
||||
internal string GetMarkerTempFilePath(PackageBundle bundle)
|
||||
{
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
string hashFolder = GetHashFolderName(bundleGuid);
|
||||
return PathUtility.Combine(RootPath, hashFolder, bundleGuid, EditorBundleCacheConsts.MarkerTempFileName);
|
||||
}
|
||||
|
||||
private string GetHashFolderName(string bundleGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bundleGuid))
|
||||
throw new YooInternalException("Bundle GUID is null or empty.");
|
||||
|
||||
if (bundleGuid.Length <= EditorBundleCacheConsts.HashFolderNameLength)
|
||||
return bundleGuid;
|
||||
return bundleGuid.Substring(0, EditorBundleCacheConsts.HashFolderNameLength);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 编辑器文件缓存常量定义
|
||||
/// </summary>
|
||||
internal static class EditorBundleCacheConsts
|
||||
{
|
||||
/// <summary>
|
||||
/// 标记文件名称
|
||||
/// </summary>
|
||||
public const string MarkerFileName = "__marker";
|
||||
|
||||
/// <summary>
|
||||
/// 标记临时文件名称
|
||||
/// </summary>
|
||||
public const string MarkerTempFileName = "__marker.tmp";
|
||||
|
||||
/// <summary>
|
||||
/// 哈希分片目录前缀长度
|
||||
/// </summary>
|
||||
public const int HashFolderNameLength = 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc5ebd7b9a6b77145b004dadfe7ed587
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -12,19 +14,46 @@ namespace YooAsset
|
||||
public string BundleGuid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源包文件路径
|
||||
/// 标记文件路径
|
||||
/// </summary>
|
||||
public string FilePath { get; private set; }
|
||||
public string MarkerFilePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建编辑器文件缓存条目实例
|
||||
/// </summary>
|
||||
/// <param name="bundleGuid">资源包唯一标识</param>
|
||||
/// <param name="filePath">资源包文件路径</param>
|
||||
public EditorBundleCacheEntry(string bundleGuid, string filePath)
|
||||
/// <param name="markerFilePath">标记文件路径</param>
|
||||
public EditorBundleCacheEntry(string bundleGuid, string markerFilePath)
|
||||
{
|
||||
BundleGuid = bundleGuid;
|
||||
FilePath = filePath;
|
||||
MarkerFilePath = markerFilePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除缓存文件夹及其所有内容
|
||||
/// </summary>
|
||||
/// <returns>删除是否成功</returns>
|
||||
public bool Delete()
|
||||
{
|
||||
try
|
||||
{
|
||||
string directory = Path.GetDirectoryName(MarkerFilePath);
|
||||
var directoryInfo = new DirectoryInfo(directory);
|
||||
if (directoryInfo.Exists)
|
||||
{
|
||||
directoryInfo.Delete(true);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
YooLogger.LogError($"Failed to delete editor cache file: {ex.Message}.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ namespace YooAsset
|
||||
{
|
||||
var cacheEntries = _fileCache.GetAllEntries();
|
||||
EvictionResult clearResult = _policy.SelectEvictionTargets(cacheEntries, _options);
|
||||
|
||||
if (clearResult.Succeeded == false)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
@@ -6,7 +5,17 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
internal sealed class EBCInitializeOperation : BCInitializeOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
ScanMarkerFiles,
|
||||
AddCacheEntries,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly EditorBundleCache _fileCache;
|
||||
private ScanMarkerFilesOperation _scanMarkerFilesOp;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
public EBCInitializeOperation(EditorBundleCache cache)
|
||||
{
|
||||
@@ -14,10 +23,58 @@ namespace YooAsset
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
SetResult();
|
||||
if (_fileCache.Config.VirtualDownloadMode == false)
|
||||
{
|
||||
SetResult();
|
||||
return;
|
||||
}
|
||||
|
||||
_steps = ESteps.ScanMarkerFiles;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.ScanMarkerFiles)
|
||||
{
|
||||
if (_scanMarkerFilesOp == null)
|
||||
{
|
||||
_scanMarkerFilesOp = new ScanMarkerFilesOperation(_fileCache);
|
||||
_scanMarkerFilesOp.StartOperation();
|
||||
AddChildOperation(_scanMarkerFilesOp);
|
||||
}
|
||||
|
||||
_scanMarkerFilesOp.UpdateOperation();
|
||||
Progress = _scanMarkerFilesOp.Progress;
|
||||
if (_scanMarkerFilesOp.IsDone == false)
|
||||
return;
|
||||
|
||||
if (_scanMarkerFilesOp.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
_steps = ESteps.AddCacheEntries;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_scanMarkerFilesOp.Error);
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.AddCacheEntries)
|
||||
{
|
||||
foreach (var fileInfo in _scanMarkerFilesOp.Result)
|
||||
{
|
||||
if (_fileCache.IsCached(fileInfo.BundleGuid) == false)
|
||||
{
|
||||
var cacheEntry = new EditorBundleCacheEntry(fileInfo.BundleGuid, fileInfo.MarkerFilePath);
|
||||
_fileCache.AddEntry(fileInfo.BundleGuid, cacheEntry);
|
||||
}
|
||||
}
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -13,15 +11,8 @@ namespace YooAsset
|
||||
|
||||
protected override void CreateBundleHandle()
|
||||
{
|
||||
string editorFilePath = EditorFileSystemHelper.GetEditorFilePath(_bundle);
|
||||
if (string.IsNullOrEmpty(editorFilePath))
|
||||
{
|
||||
SetError($"Editor file path is null. Bundle: '{_bundle.BundleName}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetResult();
|
||||
BundleHandle = new VirtualAssetBundleHandle(editorFilePath, _bundle);
|
||||
BundleHandle = new VirtualAssetBundleHandle(_bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -22,11 +21,10 @@ namespace YooAsset
|
||||
|
||||
try
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(editorFilePath);
|
||||
var rawBundle = new RawBundle(data);
|
||||
var rawBundle = RawBundleHelper.LoadFromFile(editorFilePath);
|
||||
|
||||
SetResult();
|
||||
BundleHandle = new VirtualRawBundleHandle(editorFilePath, _bundle, rawBundle);
|
||||
BundleHandle = new VirtualRawBundleHandle(_bundle, rawBundle);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
@@ -52,7 +54,38 @@ namespace YooAsset
|
||||
|
||||
if (_steps == ESteps.CacheFile)
|
||||
{
|
||||
var cacheEntry = new EditorBundleCacheEntry(_options.Bundle.BundleGuid, _options.FilePath);
|
||||
string markerFilePath = _fileCache.GetMarkerFilePath(_options.Bundle);
|
||||
string markerTempPath = _fileCache.GetMarkerTempFilePath(_options.Bundle);
|
||||
|
||||
try
|
||||
{
|
||||
// 阶段A:准备目标目录,清理可能存在的残留临时文件
|
||||
FileUtility.EnsureParentDirectoryExists(markerFilePath);
|
||||
DeleteFileSafely(markerTempPath);
|
||||
|
||||
// 阶段B:写入临时文件,内容仅用于人工调试
|
||||
string debugContent = $"BundleName={_options.Bundle.BundleName}\n"
|
||||
+ $"BundleGuid={_options.Bundle.BundleGuid}\n";
|
||||
File.WriteAllText(markerTempPath, debugContent);
|
||||
|
||||
// 阶段C:原子提交
|
||||
if (File.Exists(markerFilePath))
|
||||
File.Delete(markerFilePath);
|
||||
File.Move(markerTempPath, markerFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"Failed to write marker file: {ex.Message}.");
|
||||
YooLogger.LogError(Error);
|
||||
|
||||
// 回滚:清理临时文件,正式文件不受影响
|
||||
DeleteFileSafely(markerTempPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 阶段D:注册内存缓存条目
|
||||
var cacheEntry = new EditorBundleCacheEntry(_options.Bundle.BundleGuid, markerFilePath);
|
||||
_fileCache.AddEntry(_options.Bundle.BundleGuid, cacheEntry);
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
@@ -62,5 +95,18 @@ namespace YooAsset
|
||||
{
|
||||
ExecuteBatch();
|
||||
}
|
||||
|
||||
private static void DeleteFileSafely(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
YooLogger.LogWarning($"Failed to delete file '{filePath}': {ex.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9c86f68bc316ce54ca9396e6e6c2dd8f
|
||||
guid: 2e9a70495d8ca514caa7647851844101
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -0,0 +1,114 @@
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 扫描标记文件操作
|
||||
/// </summary>
|
||||
internal sealed class ScanMarkerFilesOperation : AsyncOperationBase
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
Prepare,
|
||||
ScanFiles,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly EditorBundleCache _fileCache;
|
||||
private IEnumerator<string> _shardEnumerator = null;
|
||||
private double _scanStartTime;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
/// <summary>
|
||||
/// 扫描到的标记件信息
|
||||
/// </summary>
|
||||
public readonly List<ScanFileInfo> Result = new List<ScanFileInfo>(5000);
|
||||
|
||||
/// <summary>
|
||||
/// 创建操作实例
|
||||
/// </summary>
|
||||
/// <param name="fileCache">编辑器文件缓存系统</param>
|
||||
internal ScanMarkerFilesOperation(EditorBundleCache fileCache)
|
||||
{
|
||||
_fileCache = fileCache;
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.Prepare;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.Prepare)
|
||||
{
|
||||
if (Directory.Exists(_fileCache.RootPath))
|
||||
{
|
||||
var directories = Directory.EnumerateDirectories(_fileCache.RootPath);
|
||||
_shardEnumerator = directories.GetEnumerator();
|
||||
_scanStartTime = TimeUtility.RealtimeSinceStartup;
|
||||
_steps = ESteps.ScanFiles;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.ScanFiles)
|
||||
{
|
||||
if (ScanFiles())
|
||||
return;
|
||||
|
||||
_shardEnumerator.Dispose();
|
||||
_shardEnumerator = null;
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
double costTime = TimeUtility.RealtimeSinceStartup - _scanStartTime;
|
||||
YooLogger.Log($"Marker file scan completed in {costTime:f1} seconds. Found {Result.Count} marker files.");
|
||||
}
|
||||
}
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
if (_shardEnumerator != null)
|
||||
{
|
||||
_shardEnumerator.Dispose();
|
||||
_shardEnumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ScanFiles()
|
||||
{
|
||||
bool hasMore;
|
||||
while (true)
|
||||
{
|
||||
hasMore = _shardEnumerator.MoveNext();
|
||||
if (hasMore == false)
|
||||
break;
|
||||
|
||||
string shardFolder = _shardEnumerator.Current;
|
||||
var childDirectories = Directory.EnumerateDirectories(shardFolder);
|
||||
foreach (string childDirectory in childDirectories)
|
||||
{
|
||||
string bundleGuid = Path.GetFileName(childDirectory);
|
||||
string markerFilePath = PathUtility.Combine(childDirectory, EditorBundleCacheConsts.MarkerFileName);
|
||||
if (File.Exists(markerFilePath))
|
||||
{
|
||||
var fileInfo = new ScanFileInfo(bundleGuid, markerFilePath);
|
||||
Result.Add(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsBusy)
|
||||
break;
|
||||
}
|
||||
|
||||
return hasMore;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc7b3752dfb973447bdae44ea8722995
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,30 @@
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// 扫描到的标记文件信息
|
||||
/// </summary>
|
||||
internal class ScanFileInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包唯一标识
|
||||
/// </summary>
|
||||
public string BundleGuid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 标记文件路径
|
||||
/// </summary>
|
||||
public string MarkerFilePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建扫描文件信息实例
|
||||
/// </summary>
|
||||
/// <param name="bundleGuid">资源包唯一标识</param>
|
||||
/// <param name="markerFilePath">标记文件路径</param>
|
||||
public ScanFileInfo(string bundleGuid, string markerFilePath)
|
||||
{
|
||||
BundleGuid = bundleGuid;
|
||||
MarkerFilePath = markerFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4a3717e25e79a14485f85e83794dd6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -19,7 +19,7 @@ namespace YooAsset
|
||||
}
|
||||
|
||||
private readonly SandboxBundleCache _fileCache;
|
||||
private IEnumerator<string> _filesEnumerator = null;
|
||||
private IEnumerator<string> _shardEnumerator = null;
|
||||
private double _verifyStartTime;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace YooAsset
|
||||
if (Directory.Exists(_fileCache.RootPath))
|
||||
{
|
||||
var directories = Directory.EnumerateDirectories(_fileCache.RootPath);
|
||||
_filesEnumerator = directories.GetEnumerator();
|
||||
_shardEnumerator = directories.GetEnumerator();
|
||||
_verifyStartTime = TimeUtility.RealtimeSinceStartup;
|
||||
_steps = ESteps.SearchFiles;
|
||||
}
|
||||
@@ -66,35 +66,35 @@ namespace YooAsset
|
||||
if (SearchFiles())
|
||||
return;
|
||||
|
||||
_filesEnumerator.Dispose();
|
||||
_filesEnumerator = null;
|
||||
_shardEnumerator.Dispose();
|
||||
_shardEnumerator = null;
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
|
||||
YooLogger.Log($"Cache file search completed in {costTime:f1} seconds.");
|
||||
YooLogger.Log($"Cache file search completed in {costTime:f1} seconds. Found {Result.Count} cache files.");
|
||||
}
|
||||
}
|
||||
protected override void InternalDispose()
|
||||
{
|
||||
if (_filesEnumerator != null)
|
||||
if (_shardEnumerator != null)
|
||||
{
|
||||
_filesEnumerator.Dispose();
|
||||
_filesEnumerator = null;
|
||||
_shardEnumerator.Dispose();
|
||||
_shardEnumerator = null;
|
||||
}
|
||||
}
|
||||
|
||||
private bool SearchFiles()
|
||||
{
|
||||
bool isFindItem;
|
||||
bool hasMore;
|
||||
while (true)
|
||||
{
|
||||
isFindItem = _filesEnumerator.MoveNext();
|
||||
if (isFindItem == false)
|
||||
hasMore = _shardEnumerator.MoveNext();
|
||||
if (hasMore == false)
|
||||
break;
|
||||
|
||||
var rootFolder = _filesEnumerator.Current;
|
||||
var childDirectories = Directory.EnumerateDirectories(rootFolder);
|
||||
var shardFolder = _shardEnumerator.Current;
|
||||
var childDirectories = Directory.EnumerateDirectories(shardFolder);
|
||||
foreach (var childDirectory in childDirectories)
|
||||
{
|
||||
string bundleGuid = Path.GetFileName(childDirectory);
|
||||
@@ -113,7 +113,7 @@ namespace YooAsset
|
||||
break;
|
||||
}
|
||||
|
||||
return isFindItem;
|
||||
return hasMore;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,8 @@ namespace YooAsset
|
||||
var options = new LoadLocalArchiveBundleOptions(
|
||||
cacheName: _fileCache.GetType().Name,
|
||||
bundle: _bundle,
|
||||
filePath: _cacheEntry.DataFilePath);
|
||||
filePath: _cacheEntry.DataFilePath,
|
||||
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor);
|
||||
_loadLocalArchiveBundleOp = new LoadLocalArchiveBundleOperation(options);
|
||||
_loadLocalArchiveBundleOp.StartOperation();
|
||||
AddChildOperation(_loadLocalArchiveBundleOp);
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace YooAsset
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = new AssetBundleHandle(_cacheEntry.DataFilePath, _bundle, assetBundle, null);
|
||||
BundleHandle = new AssetBundleHandle(_bundle, assetBundle, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,23 +30,29 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IBundleDecryptor RawBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ArchiveBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor ArchiveBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// AssetBundle 备用解密器
|
||||
/// </summary>
|
||||
public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; }
|
||||
|
||||
public Configuration(int fileVerifyMaxConcurrency, EFileVerifyLevel fileVerifyLevel,
|
||||
IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IBundleMemoryDecryptor assetBundleFallbackDecryptor)
|
||||
IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
|
||||
IBundleDecryptor archiveBundleDecryptor, IBundleMemoryDecryptor assetBundleFallbackDecryptor)
|
||||
{
|
||||
FileVerifyMaxConcurrency = fileVerifyMaxConcurrency;
|
||||
FileVerifyLevel = fileVerifyLevel;
|
||||
AssetBundleDecryptor = assetBundleDecryptor;
|
||||
RawBundleDecryptor = rawBundleDecryptor;
|
||||
ArchiveBundleDecryptor = archiveBundleDecryptor;
|
||||
AssetBundleFallbackDecryptor = assetBundleFallbackDecryptor;
|
||||
}
|
||||
}
|
||||
|
||||
private const int HashFolderNameLength = 2;
|
||||
private readonly Dictionary<string, SandboxBundleCacheEntry> _cacheEntries = new Dictionary<string, SandboxBundleCacheEntry>(10000);
|
||||
private readonly Dictionary<string, string> _dataFilePathMapping = new Dictionary<string, string>(10000);
|
||||
private readonly Dictionary<string, string> _infoFilePathMapping = new Dictionary<string, string>(10000);
|
||||
@@ -154,6 +160,17 @@ namespace YooAsset
|
||||
{
|
||||
return _cacheEntries.ContainsKey(bundleGuid);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public string GetCacheFilePath(string bundleGuid)
|
||||
{
|
||||
if (_cacheEntries.TryGetValue(bundleGuid, out SandboxBundleCacheEntry entry))
|
||||
{
|
||||
return entry.DataFilePath;
|
||||
}
|
||||
|
||||
YooLogger.LogWarning($"Cache file path not found. Bundle GUID: '{bundleGuid}'.");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据 ClearMethod 创建对应的淘汰策略实例
|
||||
@@ -185,11 +202,12 @@ namespace YooAsset
|
||||
/// <returns>数据文件的完整路径</returns>
|
||||
internal string GetDataFilePath(PackageBundle bundle)
|
||||
{
|
||||
if (_dataFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
if (_dataFilePathMapping.TryGetValue(bundleGuid, out string filePath) == false)
|
||||
{
|
||||
string folderName = GetHashFolderName(bundle.FileHash);
|
||||
filePath = PathUtility.Combine(RootPath, folderName, bundle.BundleGuid, SandboxBundleCacheConsts.BundleDataFileName);
|
||||
_dataFilePathMapping.Add(bundle.BundleGuid, filePath);
|
||||
string folderName = GetHashFolderName(bundleGuid);
|
||||
filePath = PathUtility.Combine(RootPath, folderName, bundleGuid, SandboxBundleCacheConsts.BundleDataFileName);
|
||||
_dataFilePathMapping.Add(bundleGuid, filePath);
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
@@ -201,11 +219,12 @@ namespace YooAsset
|
||||
/// <returns>信息文件的完整路径</returns>
|
||||
internal string GetInfoFilePath(PackageBundle bundle)
|
||||
{
|
||||
if (_infoFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
if (_infoFilePathMapping.TryGetValue(bundleGuid, out string filePath) == false)
|
||||
{
|
||||
string folderName = GetHashFolderName(bundle.FileHash);
|
||||
filePath = PathUtility.Combine(RootPath, folderName, bundle.BundleGuid, SandboxBundleCacheConsts.BundleInfoFileName);
|
||||
_infoFilePathMapping.Add(bundle.BundleGuid, filePath);
|
||||
string folderName = GetHashFolderName(bundleGuid);
|
||||
filePath = PathUtility.Combine(RootPath, folderName, bundleGuid, SandboxBundleCacheConsts.BundleInfoFileName);
|
||||
_infoFilePathMapping.Add(bundleGuid, filePath);
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
@@ -217,8 +236,9 @@ namespace YooAsset
|
||||
/// <returns>数据临时文件的完整路径</returns>
|
||||
internal string GetDataTempFilePath(PackageBundle bundle)
|
||||
{
|
||||
string folderName = GetHashFolderName(bundle.FileHash);
|
||||
return PathUtility.Combine(RootPath, folderName, bundle.BundleGuid, SandboxBundleCacheConsts.BundleDataTempFileName);
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
string folderName = GetHashFolderName(bundleGuid);
|
||||
return PathUtility.Combine(RootPath, folderName, bundleGuid, SandboxBundleCacheConsts.BundleDataTempFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -228,8 +248,9 @@ namespace YooAsset
|
||||
/// <returns>信息临时文件的完整路径</returns>
|
||||
internal string GetInfoTempFilePath(PackageBundle bundle)
|
||||
{
|
||||
string folderName = GetHashFolderName(bundle.FileHash);
|
||||
return PathUtility.Combine(RootPath, folderName, bundle.BundleGuid, SandboxBundleCacheConsts.BundleInfoTempFileName);
|
||||
string bundleGuid = bundle.BundleGuid;
|
||||
string folderName = GetHashFolderName(bundleGuid);
|
||||
return PathUtility.Combine(RootPath, folderName, bundleGuid, SandboxBundleCacheConsts.BundleInfoTempFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -286,14 +307,14 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
|
||||
private string GetHashFolderName(string fileHash)
|
||||
private string GetHashFolderName(string bundleGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fileHash))
|
||||
throw new YooInternalException("File hash is null or empty.");
|
||||
if (string.IsNullOrEmpty(bundleGuid))
|
||||
throw new YooInternalException("Bundle GUID is null or empty.");
|
||||
|
||||
if (fileHash.Length <= HashFolderNameLength)
|
||||
return fileHash;
|
||||
return fileHash.Substring(0, HashFolderNameLength);
|
||||
if (bundleGuid.Length <= SandboxBundleCacheConsts.HashFolderNameLength)
|
||||
return bundleGuid;
|
||||
return bundleGuid.Substring(0, SandboxBundleCacheConsts.HashFolderNameLength);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -40,5 +40,10 @@ namespace YooAsset
|
||||
/// 信息文件预期大小(字节)
|
||||
/// </summary>
|
||||
public const int InfoFileExpectedSize = 36;
|
||||
|
||||
/// <summary>
|
||||
/// 哈希分片目录前缀长度
|
||||
/// </summary>
|
||||
public const int HashFolderNameLength = 2;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f71ffbb8bf041e5478862cc179cd7e3f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b2ea6881f51a434db344d28e4e5f410
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5aefbf5ae866434483465f633edf635
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,182 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 游戏平台缓存系统
|
||||
/// </summary>
|
||||
internal class WebGameBundleCache : IBundleCache
|
||||
{
|
||||
internal readonly struct Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏平台接口
|
||||
/// </summary>
|
||||
public IWebGamePlatform GamePlatform { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 看门狗超时时间
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 禁用 Unity 内置网络缓存
|
||||
/// </summary>
|
||||
public bool DisableUnityWebCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载数据校验级别
|
||||
/// </summary>
|
||||
public EFileVerifyLevel DownloadVerifyLevel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// AssetBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor AssetBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务接口
|
||||
/// </summary>
|
||||
public IRemoteService RemoteService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载后台接口
|
||||
/// </summary>
|
||||
public IDownloadBackend DownloadBackend { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载重试判定策略
|
||||
/// </summary>
|
||||
public IDownloadRetryPolicy DownloadRetryPolicy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// URL 选择策略
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public Configuration(IWebGamePlatform gamePlatform,
|
||||
int watchdogTimeout, bool disableUnityWebCache,
|
||||
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IRemoteService remoteService,
|
||||
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
GamePlatform = gamePlatform;
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DisableUnityWebCache = disableUnityWebCache;
|
||||
DownloadVerifyLevel = downloadVerifyLevel;
|
||||
AssetBundleDecryptor = assetBundleDecryptor;
|
||||
RemoteService = remoteService;
|
||||
DownloadBackend = downloadBackend;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
DownloadUrlPolicy = downloadUrlPolicy;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, WebGameBundleCacheEntry> _cacheEntries = new Dictionary<string, WebGameBundleCacheEntry>(10000);
|
||||
|
||||
/// <summary>
|
||||
/// 缓存配置
|
||||
/// </summary>
|
||||
internal readonly Configuration Config;
|
||||
|
||||
#region 接口属性
|
||||
/// <inheritdoc/>
|
||||
public string PackageName { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string RootPath { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool IsReadOnly { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FileCount => _cacheEntries.Count;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long SpaceOccupied => 0;
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 创建 WebGameBundleCache 实例
|
||||
/// </summary>
|
||||
/// <param name="packageName">包裹名称</param>
|
||||
/// <param name="rootPath">缓存根目录</param>
|
||||
/// <param name="config">缓存配置</param>
|
||||
public WebGameBundleCache(string packageName, string rootPath, Configuration config)
|
||||
{
|
||||
PackageName = packageName;
|
||||
RootPath = rootPath;
|
||||
Config = config;
|
||||
IsReadOnly = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public BCInitializeOperation InitializeAsync()
|
||||
{
|
||||
var operation = new WGBCInitializeOperation();
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
|
||||
{
|
||||
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebGameBundleCache)} is readonly.");
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
|
||||
{
|
||||
var operation = new BCClearCacheCompleteOperation();
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
|
||||
{
|
||||
var operation = new BCVerifyCacheCompleteOperation();
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
|
||||
{
|
||||
if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
|
||||
{
|
||||
var operation = new WGBCLoadAssetBundleOperation(this, options);
|
||||
return operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"{nameof(WebGameBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
|
||||
var operation = new BCLoadBundleErrorOperation(error);
|
||||
return operation;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool IsCached(string bundleGuid)
|
||||
{
|
||||
if (_cacheEntries.TryGetValue(bundleGuid, out var entry))
|
||||
return Config.GamePlatform.IsCached(entry.CacheFilePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
#region 内部方法
|
||||
/// <summary>
|
||||
/// 获取或创建指定资源包的缓存条目
|
||||
/// </summary>
|
||||
/// <param name="bundle">资源包描述</param>
|
||||
/// <returns>已存在则返回对应条目;否则创建并登记后返回新条目。</returns>
|
||||
internal WebGameBundleCacheEntry GetEntry(PackageBundle bundle)
|
||||
{
|
||||
if (_cacheEntries.TryGetValue(bundle.BundleGuid, out var entry))
|
||||
return entry;
|
||||
|
||||
var urls = Config.RemoteService.GetRemoteUrls(bundle.GetFileName());
|
||||
var filePath = Config.GamePlatform.GetCacheFilePath(RootPath, bundle);
|
||||
var newEntry = new WebGameBundleCacheEntry(bundle.BundleGuid, urls, filePath);
|
||||
_cacheEntries.Add(bundle.BundleGuid, newEntry);
|
||||
return newEntry;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ddef4533d379a984c8d1669776147acf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,38 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 游戏平台缓存条目
|
||||
/// </summary>
|
||||
internal class WebGameBundleCacheEntry : ICacheEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源包唯一标识
|
||||
/// </summary>
|
||||
public string BundleGuid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 候选下载地址列表
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台侧缓存文件路径
|
||||
/// </summary>
|
||||
public string CacheFilePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建 WebGL 游戏平台缓存条目实例
|
||||
/// </summary>
|
||||
/// <param name="bundleGuid">资源包唯一标识</param>
|
||||
/// <param name="urls">候选下载地址列表</param>
|
||||
/// <param name="cacheFilePath">平台侧缓存文件路径</param>
|
||||
public WebGameBundleCacheEntry(string bundleGuid, IReadOnlyList<string> urls, string cacheFilePath)
|
||||
{
|
||||
BundleGuid = bundleGuid;
|
||||
Urls = urls;
|
||||
CacheFilePath = cacheFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f11e7e5e45702bb459771f57e1b84fdc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6cbd76495075b54993179318fdda8dc
|
||||
guid: c284033155b7a7f4f9bcd7fa16e8f169
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ead67fdda05e99747b03f49accc1526a
|
||||
guid: 9494c5da1a02a7b43b40729e88fbda21
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
@@ -2,9 +2,9 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 游戏平台缓存系统初始化操作
|
||||
/// WebGL 平台网络缓存初始化操作
|
||||
/// </summary>
|
||||
internal sealed class WGBCInitializeOperation : BCInitializeOperation
|
||||
internal sealed class WNBCInitializeOperation : BCInitializeOperation
|
||||
{
|
||||
protected override void InternalStart()
|
||||
{
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8a286ded9b4bbc340959221a542c8885
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,86 @@
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 平台网络缓存加载 ArchiveBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class WNBCLoadArchiveBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
LoadBundle,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly WebNetworkBundleCache _fileCache;
|
||||
private readonly BCLoadBundleOptions _options;
|
||||
private BCLoadBundleOperation _loadBundleOp;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal WNBCLoadArchiveBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
|
||||
{
|
||||
_fileCache = fileCache;
|
||||
_options = options;
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.LoadBundle;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.LoadBundle)
|
||||
{
|
||||
if (_loadBundleOp == null)
|
||||
{
|
||||
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
|
||||
var options = new LoadWebArchiveBundleOptions(
|
||||
cacheName: _fileCache.GetType().Name,
|
||||
bundle: _options.Bundle,
|
||||
candidateUrls: urls,
|
||||
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor,
|
||||
downloadBackend: _fileCache.Config.DownloadBackend,
|
||||
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
|
||||
watchdogTimeout: 0,
|
||||
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
|
||||
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
|
||||
_loadBundleOp = new LoadWebArchiveBundleOperation(options);
|
||||
_loadBundleOp.StartOperation();
|
||||
AddChildOperation(_loadBundleOp);
|
||||
}
|
||||
|
||||
_loadBundleOp.UpdateOperation();
|
||||
Progress = _loadBundleOp.Progress;
|
||||
if (_loadBundleOp.IsDone == false)
|
||||
return;
|
||||
|
||||
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
if (_loadBundleOp.BundleHandle == null)
|
||||
throw new YooInternalException("Loaded bundle handle is null.");
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = _loadBundleOp.BundleHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError(_loadBundleOp.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
protected override void InternalWaitForCompletion()
|
||||
{
|
||||
if (_steps != ESteps.Done)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError("WebGL platform does not support synchronous loading.");
|
||||
YooLogger.LogError(Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b06ab626f2f3afe4e94f6ceaaaf1ba2c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,82 +1,68 @@
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// WebGL 游戏平台加载 AssetBundle 操作
|
||||
/// WebGL 平台网络缓存加载 AssetBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class WGBCLoadAssetBundleOperation : BCLoadBundleOperation
|
||||
internal sealed class WNBCLoadAssetBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
None,
|
||||
GetEntry,
|
||||
LoadBundle,
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly WebGameBundleCache _fileCache;
|
||||
private readonly WebNetworkBundleCache _fileCache;
|
||||
private readonly BCLoadBundleOptions _options;
|
||||
private BCLoadBundleOperation _loadBundleOp;
|
||||
private WebGameBundleCacheEntry _cacheEntry;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal WGBCLoadAssetBundleOperation(WebGameBundleCache fileCache, BCLoadBundleOptions options)
|
||||
internal WNBCLoadAssetBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
|
||||
{
|
||||
_fileCache = fileCache;
|
||||
_options = options;
|
||||
}
|
||||
protected override void InternalStart()
|
||||
{
|
||||
_steps = ESteps.GetEntry;
|
||||
_steps = ESteps.LoadBundle;
|
||||
}
|
||||
protected override void InternalUpdate()
|
||||
{
|
||||
if (_steps == ESteps.None || _steps == ESteps.Done)
|
||||
return;
|
||||
|
||||
if (_steps == ESteps.GetEntry)
|
||||
{
|
||||
_cacheEntry = _fileCache.GetEntry(_options.Bundle);
|
||||
if (_cacheEntry == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError($"File cache entry not found: '{_options.Bundle.BundleGuid}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.LoadBundle;
|
||||
}
|
||||
}
|
||||
|
||||
if (_steps == ESteps.LoadBundle)
|
||||
{
|
||||
if (_loadBundleOp == null)
|
||||
{
|
||||
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
|
||||
if (_options.Bundle.IsEncrypted)
|
||||
{
|
||||
var options = new LoadWebAssetBundleOptions(
|
||||
var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
|
||||
cacheName: _fileCache.GetType().Name,
|
||||
bundle: _options.Bundle,
|
||||
candidateUrls: _cacheEntry.Urls,
|
||||
candidateUrls: urls,
|
||||
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
|
||||
downloadBackend: _fileCache.Config.DownloadBackend,
|
||||
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
|
||||
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
|
||||
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
|
||||
watchdogTimeout: 0,
|
||||
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
|
||||
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
|
||||
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
|
||||
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
var webGameOptions = new LoadWebGameAssetBundleOptions(
|
||||
var platformOptions = new LoadWebPlatformAssetBundleOptions(
|
||||
bundle: _options.Bundle,
|
||||
candidateUrls: _cacheEntry.Urls,
|
||||
gamePlatform: _fileCache.Config.GamePlatform,
|
||||
cacheFilePath: _cacheEntry.CacheFilePath,
|
||||
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
|
||||
candidateUrls: urls,
|
||||
platformStrategy: _fileCache.Config.PlatformStrategy,
|
||||
downloadBackend: _fileCache.Config.DownloadBackend,
|
||||
watchdogTimeout: 0,
|
||||
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
|
||||
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
|
||||
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
|
||||
_loadBundleOp = new LoadWebGameAssetBundleOperation(webGameOptions);
|
||||
_loadBundleOp = new LoadWebPlatformAssetBundleOperation(platformOptions);
|
||||
}
|
||||
|
||||
_loadBundleOp.StartOperation();
|
||||
@@ -114,4 +100,4 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d23033bab8f508240af14aea1d1c9776
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -2,9 +2,9 @@
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// Web远端文件系统的加载资源包操作
|
||||
/// WebGL 平台网络缓存加载 RawBundle 操作
|
||||
/// </summary>
|
||||
internal sealed class WRFSLoadPackageBundleOperation : FSLoadPackageBundleOperation
|
||||
internal sealed class WNBCLoadRawBundleOperation : BCLoadBundleOperation
|
||||
{
|
||||
private enum ESteps
|
||||
{
|
||||
@@ -13,14 +13,14 @@ namespace YooAsset
|
||||
Done,
|
||||
}
|
||||
|
||||
private readonly WebRemoteFileSystem _fileSystem;
|
||||
private readonly FSLoadPackageBundleOptions _options;
|
||||
private readonly WebNetworkBundleCache _fileCache;
|
||||
private readonly BCLoadBundleOptions _options;
|
||||
private BCLoadBundleOperation _loadBundleOp;
|
||||
private ESteps _steps = ESteps.None;
|
||||
|
||||
internal WRFSLoadPackageBundleOperation(WebRemoteFileSystem fileSystem, FSLoadPackageBundleOptions options)
|
||||
internal WNBCLoadRawBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
_fileCache = fileCache;
|
||||
_options = options;
|
||||
}
|
||||
protected override void InternalStart()
|
||||
@@ -36,7 +36,18 @@ namespace YooAsset
|
||||
{
|
||||
if (_loadBundleOp == null)
|
||||
{
|
||||
_loadBundleOp = _fileSystem.BundleCache.LoadBundleAsync(_options.ConvertTo());
|
||||
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
|
||||
var options = new LoadWebRawBundleOptions(
|
||||
cacheName: _fileCache.GetType().Name,
|
||||
bundle: _options.Bundle,
|
||||
candidateUrls: urls,
|
||||
rawBundleDecryptor: _fileCache.Config.RawBundleDecryptor,
|
||||
downloadBackend: _fileCache.Config.DownloadBackend,
|
||||
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
|
||||
watchdogTimeout: 0,
|
||||
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
|
||||
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
|
||||
_loadBundleOp = new LoadWebRawBundleOperation(options);
|
||||
_loadBundleOp.StartOperation();
|
||||
AddChildOperation(_loadBundleOp);
|
||||
}
|
||||
@@ -49,16 +60,11 @@ namespace YooAsset
|
||||
if (_loadBundleOp.Status == EOperationStatus.Succeeded)
|
||||
{
|
||||
if (_loadBundleOp.BundleHandle == null)
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetError("Fatal error: loaded bundle handle is null.");
|
||||
}
|
||||
else
|
||||
{
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = _loadBundleOp.BundleHandle;
|
||||
}
|
||||
throw new YooInternalException("Loaded bundle handle is null.");
|
||||
|
||||
_steps = ESteps.Done;
|
||||
SetResult();
|
||||
BundleHandle = _loadBundleOp.BundleHandle;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,4 +83,4 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2757bc29c7400b24c8bd4f250e414b65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,20 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace YooAsset
|
||||
{
|
||||
/// <summary>
|
||||
/// Web远端文件缓存系统,用于从远程服务器加载资源。
|
||||
/// WebGL 平台网络缓存系统
|
||||
/// </summary>
|
||||
internal class WebRemoteBundleCache : IBundleCache
|
||||
internal class WebNetworkBundleCache : IBundleCache
|
||||
{
|
||||
internal readonly struct Configuration
|
||||
{
|
||||
/// <summary>
|
||||
/// 看门狗超时时间
|
||||
/// </summary>
|
||||
public int WatchdogTimeout { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 禁用 Unity 内置网络缓存
|
||||
/// </summary>
|
||||
@@ -30,6 +22,21 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IBundleDecryptor AssetBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// RawBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor RawBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ArchiveBundle 解密器
|
||||
/// </summary>
|
||||
public IBundleDecryptor ArchiveBundleDecryptor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 平台策略接口
|
||||
/// </summary>
|
||||
public IWebPlatformStrategy PlatformStrategy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 远程服务接口
|
||||
/// </summary>
|
||||
@@ -50,14 +57,17 @@ namespace YooAsset
|
||||
/// </summary>
|
||||
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
|
||||
|
||||
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
|
||||
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IRemoteService remoteService,
|
||||
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
public Configuration(bool disableUnityWebCache,
|
||||
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
|
||||
IBundleDecryptor archiveBundleDecryptor, IWebPlatformStrategy platformStrategy, IRemoteService remoteService,
|
||||
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
|
||||
{
|
||||
WatchdogTimeout = watchdogTimeout;
|
||||
DisableUnityWebCache = disableUnityWebCache;
|
||||
DownloadVerifyLevel = downloadVerifyLevel;
|
||||
AssetBundleDecryptor = assetBundleDecryptor;
|
||||
RawBundleDecryptor = rawBundleDecryptor;
|
||||
ArchiveBundleDecryptor = archiveBundleDecryptor;
|
||||
PlatformStrategy = platformStrategy;
|
||||
RemoteService = remoteService;
|
||||
DownloadBackend = downloadBackend;
|
||||
DownloadRetryPolicy = downloadRetryPolicy;
|
||||
@@ -65,8 +75,6 @@ namespace YooAsset
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, WebRemoteBundleCacheEntry> _cacheEntries = new Dictionary<string, WebRemoteBundleCacheEntry>(10000);
|
||||
|
||||
/// <summary>
|
||||
/// 缓存配置
|
||||
/// </summary>
|
||||
@@ -83,28 +91,15 @@ namespace YooAsset
|
||||
public bool IsReadOnly { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public int FileCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cacheEntries.Count;
|
||||
}
|
||||
}
|
||||
public int FileCount { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public long SpaceOccupied { get; private set; }
|
||||
public long SpaceOccupied { get; }
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 创建 WebRemoteBundleCache 实例
|
||||
/// </summary>
|
||||
/// <param name="packageName">包裹名称</param>
|
||||
/// <param name="rootPath">缓存根目录</param>
|
||||
/// <param name="config">缓存配置</param>
|
||||
public WebRemoteBundleCache(string packageName, string rootPath, Configuration config)
|
||||
public WebNetworkBundleCache(string packageName, Configuration config)
|
||||
{
|
||||
PackageName = packageName;
|
||||
RootPath = rootPath;
|
||||
Config = config;
|
||||
IsReadOnly = true;
|
||||
}
|
||||
@@ -116,13 +111,13 @@ namespace YooAsset
|
||||
/// <inheritdoc />
|
||||
public BCInitializeOperation InitializeAsync()
|
||||
{
|
||||
var operation = new WRBCInitializeOperation(this);
|
||||
var operation = new WNBCInitializeOperation();
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
|
||||
{
|
||||
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebRemoteBundleCache)} is readonly.");
|
||||
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebNetworkBundleCache)} is readonly.");
|
||||
return operation;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
@@ -142,12 +137,22 @@ namespace YooAsset
|
||||
{
|
||||
if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
|
||||
{
|
||||
var operation = new WRBCLoadAssetBundleOperation(this, options);
|
||||
var operation = new WNBCLoadAssetBundleOperation(this, options);
|
||||
return operation;
|
||||
}
|
||||
else if (options.Bundle.GetBundleType() == (int)EBundleType.RawBundle)
|
||||
{
|
||||
var operation = new WNBCLoadRawBundleOperation(this, options);
|
||||
return operation;
|
||||
}
|
||||
else if (options.Bundle.GetBundleType() == (int)EBundleType.ArchiveBundle)
|
||||
{
|
||||
var operation = new WNBCLoadArchiveBundleOperation(this, options);
|
||||
return operation;
|
||||
}
|
||||
else
|
||||
{
|
||||
string error = $"{nameof(WebRemoteBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
|
||||
string error = $"{nameof(WebNetworkBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
|
||||
var operation = new BCLoadBundleErrorOperation(error);
|
||||
return operation;
|
||||
}
|
||||
@@ -157,23 +162,11 @@ namespace YooAsset
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#region 内部方法
|
||||
/// <summary>
|
||||
/// 获取或创建指定资源包的缓存条目
|
||||
/// </summary>
|
||||
/// <param name="bundle">资源包描述</param>
|
||||
/// <returns>已存在则返回对应条目;否则创建并登记后返回新条目。</returns>
|
||||
internal WebRemoteBundleCacheEntry GetEntry(PackageBundle bundle)
|
||||
/// <inheritdoc />
|
||||
public string GetCacheFilePath(string bundleGuid)
|
||||
{
|
||||
if (_cacheEntries.TryGetValue(bundle.BundleGuid, out WebRemoteBundleCacheEntry entry))
|
||||
return entry;
|
||||
|
||||
var urls = Config.RemoteService.GetRemoteUrls(bundle.GetFileName());
|
||||
var newEntry = new WebRemoteBundleCacheEntry(bundle.BundleGuid, urls);
|
||||
_cacheEntries.Add(bundle.BundleGuid, newEntry);
|
||||
return newEntry;
|
||||
YooLogger.LogWarning($"{nameof(WebNetworkBundleCache)} does not support local cache file path.");
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user