Compare commits

...

23 Commits

Author SHA1 Message Date
何冠峰
f631d67db3 Update CHANGELOG.md 2026-05-28 22:12:28 +08:00
何冠峰
a2fa8ff6fc Update package.json 2026-05-28 22:12:19 +08:00
何冠峰
477954377d Update EPlayMode.cs 2026-05-28 21:48:11 +08:00
何冠峰
513c1d0b18 feat: support ArchiveBundle loading in Web file systems 2026-05-28 21:42:07 +08:00
何冠峰
289dee326b feat : support encrypted ArchiveBundle 2026-05-28 20:43:22 +08:00
何冠峰
d5a9b9f0f4 Add explicit YooAsset macro gates 2026-05-28 11:03:28 +08:00
何冠峰
aff50317cb feat : add IBuiltinFileAccessor 2026-05-28 10:37:42 +08:00
何冠峰
9e969d34f6 feat : add IBundleUnpackPolicy 2026-05-27 14:33:31 +08:00
何冠峰
926f72d129 update space shooter 2026-05-27 10:36:35 +08:00
何冠峰
72c5a9588e feat : add WebNetwork file system 2026-05-27 10:31:35 +08:00
何冠峰
af7aecbf0b change : remove legacy WebGame and WebRemote file systems 2026-05-27 10:31:15 +08:00
何冠峰
d337a86e68 feat : add WebNetwork file system 2026-05-26 19:18:39 +08:00
何冠峰
9c05f3514e fix #696 2026-05-25 19:44:31 +08:00
何冠峰
b31bb1bcfb feat: add collect asset search to Bundle Collector 2026-05-25 19:38:17 +08:00
何冠峰
92e34fb8b5 feat : web remote file system support raw bundle 2026-05-21 22:08:13 +08:00
何冠峰
687dc1d9e9 feat : web server file system support raw bundle 2026-05-21 22:07:44 +08:00
何冠峰
7842235af6 feat : web game file system support raw bundle 2026-05-21 21:44:51 +08:00
何冠峰
0142828b91 docs: unify runtime options comments 2026-05-21 18:43:22 +08:00
何冠峰
a36a7cc6d2 feat : kuaishou file system support 2026-05-21 17:30:59 +08:00
何冠峰
485ed5198e feat : vivo file system support 2026-05-21 17:17:50 +08:00
何冠峰
2170156216 feat : oppo file system support 2026-05-21 17:17:37 +08:00
何冠峰
99b975554a refactor: remove WebGame platform cache query APIs
移除 WebGame 平台缓存查询接口
2026-05-21 14:34:07 +08:00
何冠峰
2f8355c3f4 feat : instant asset build pipeline 2026-05-21 14:06:26 +08:00
332 changed files with 4775 additions and 2130 deletions

View File

@@ -2,6 +2,50 @@
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

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -1,4 +1,4 @@
#if TUANJIE_1_8_OR_NEWER
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
namespace YooAsset.Editor
{
/// <summary>

View File

@@ -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

View File

@@ -1,4 +1,4 @@
#if TUANJIE_1_8_OR_NEWER
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
namespace YooAsset.Editor
{
/// <summary>

View File

@@ -1,4 +1,4 @@
#if TUANJIE_1_8_OR_NEWER
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
namespace YooAsset.Editor
{
/// <summary>

View File

@@ -1,4 +1,4 @@
#if TUANJIE_1_8_OR_NEWER
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
namespace YooAsset.Editor
{
/// <summary>

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 9c86f68bc316ce54ca9396e6e6c2dd8f
guid: 07c436f0dfa2bcf43b12628aed2aa91c
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -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;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4c79e03ac8bcbef4aa0e2eede5bf63fb
guid: ff1a09e6aca8b104c9b256885b06130f
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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);
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 1cfc5979929f8cd43b58e1fcc541eaa8
guid: 6c8bc2931bda7884198893f1d2e8f364
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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,
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 5ef65b981b91200439b05f29435e7c92
guid: 9a2c8a878d6041b4d8bad2a2d2f1896d
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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">

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 清理缓存操作选项
/// 清理缓存操作选项
/// </summary>
internal readonly struct BCClearCacheOptions
{

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 加载资源包操作选项
/// 加载资源包操作选项
/// </summary>
internal readonly struct BCLoadBundleOptions
{

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 验证缓存操作选项
/// 验证缓存操作选项
/// </summary>
internal readonly struct BCVerifyCacheOptions
{

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 写入缓存操作选项
/// 写入缓存操作选项
/// </summary>
internal readonly struct BCWriteCacheOptions
{

View File

@@ -31,7 +31,7 @@ namespace YooAsset
/// <summary>
/// 创建加载内置资源目录操作实例
/// </summary>
/// <param name="options">加载内置资源目录的配置选项</param>
/// <param name="options">加载内置资源目录的操作选项</param>
internal LoadBuiltinCatalogOperation(LoadBuiltinCatalogOptions options)
{
_options = options;

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 加载内置资源目录操作选项
/// 加载内置资源目录操作选项
/// </summary>
internal readonly struct LoadBuiltinCatalogOptions
{

View File

@@ -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;
@@ -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}.");
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
@@ -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();
}

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 加载 AssetBundle 的上下文信息
/// 本地加载 AssetBundle 的操作选项
/// </summary>
internal readonly struct LoadLocalAssetBundleOptions
{

View File

@@ -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;
}
@@ -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();
}
}

View File

@@ -2,7 +2,7 @@
namespace YooAsset
{
/// <summary>
/// 加载 RawBundle 的上下文信息
/// 本地加载 RawBundle 的操作选项
/// </summary>
internal readonly struct LoadLocalRawBundleOptions
{

View File

@@ -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}.");
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f0fd9af541471154d9fc968abd450d31
guid: 00ec926bdc7c65e40a464570d27570d9
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -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;
}
}
}

View File

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

View File

@@ -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(_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,7 +164,7 @@ namespace YooAsset
if (assetBundle == null)
{
_steps = ESteps.Done;
SetError("Unity engine load failed.");
SetError("Unity engine load asset bundle failed.");
}
else
{

View File

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

View File

@@ -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;
}

View File

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

View File

@@ -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.Bundle, assetBundle, _options.GamePlatform);
BundleHandle = new WebAssetBundleHandle(_options.Bundle, assetBundle, _options.PlatformStrategy);
}
}
else

View File

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

View File

@@ -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,15 +18,25 @@ namespace YooAsset
public IReadOnlyList<string> CandidateUrls { get; }
/// <summary>
/// 游戏平台接口
/// 平台策略
/// </summary>
public IWebGamePlatform GamePlatform { get; }
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary>
/// 下载后台接口
/// </summary>
public IDownloadBackend DownloadBackend { get; }
/// <summary>
/// 看门狗超时时间
/// </summary>
public int WatchdogTimeout { get; }
/// <summary>
/// 禁用 Unity 内置网络缓存
/// </summary>
public bool DisableUnityWebCache { get; }
/// <summary>
/// 下载重试判定策略
/// </summary>
@@ -37,14 +47,16 @@ namespace YooAsset
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public LoadWebGameAssetBundleOptions(PackageBundle bundle, IReadOnlyList<string> candidateUrls,
IWebGamePlatform gamePlatform, 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;
PlatformStrategy = platformStrategy;
DownloadBackend = downloadBackend;
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}

View File

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

View File

@@ -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}.");
}
}
}
}

View File

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

View File

@@ -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;
}
}
}

View File

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

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -1,5 +1,4 @@
using System;
using System.IO;
namespace YooAsset
{
@@ -22,8 +21,7 @@ namespace YooAsset
try
{
byte[] data = File.ReadAllBytes(editorFilePath);
var rawBundle = new RawBundle(data);
var rawBundle = RawBundleHelper.LoadFromFile(editorFilePath);
SetResult();
BundleHandle = new VirtualRawBundleHandle(_bundle, rawBundle);

View File

@@ -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);

View File

@@ -30,18 +30,25 @@ 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;
}
}

View File

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

View File

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

View File

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

View File

@@ -1,165 +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;
}
}
/// <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 { get; }
/// <inheritdoc/>
public long SpaceOccupied { get; }
#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)
{
return true;
}
/// <inheritdoc/>
public string GetCacheFilePath(string bundleGuid)
{
YooLogger.LogWarning($"{nameof(WebGameBundleCache)} does not support local cache file path.");
return null;
}
}
}

View File

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

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: f6cbd76495075b54993179318fdda8dc
guid: c284033155b7a7f4f9bcd7fa16e8f169
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: ead67fdda05e99747b03f49accc1526a
guid: 9494c5da1a02a7b43b40729e88fbda21
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -2,9 +2,9 @@
namespace YooAsset
{
/// <summary>
/// WebGL 游戏平台缓存系统初始化操作
/// WebGL 平台网络缓存初始化操作
/// </summary>
internal sealed class WGBCInitializeOperation : BCInitializeOperation
internal sealed class WNBCInitializeOperation : BCInitializeOperation
{
protected override void InternalStart()
{

View File

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

View File

@@ -2,9 +2,9 @@
namespace YooAsset
{
/// <summary>
/// Web远端文件缓存加载 AssetBundle 操作
/// WebGL 平台网络缓存加载 ArchiveBundle 操作
/// </summary>
internal sealed class WRBCLoadAssetBundleOperation : BCLoadBundleOperation
internal sealed class WNBCLoadArchiveBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
@@ -13,17 +13,12 @@ namespace YooAsset
Done,
}
private readonly WebRemoteBundleCache _fileCache;
private readonly WebNetworkBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
/// <summary>
/// 创建 WRBCLoadAssetBundleOperation 实例
/// </summary>
/// <param name="fileCache">Web 远端文件缓存系统</param>
/// <param name="options">加载资源包操作选项</param>
public WRBCLoadAssetBundleOperation(WebRemoteBundleCache fileCache, BCLoadBundleOptions options)
internal WNBCLoadArchiveBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
@@ -42,23 +37,17 @@ namespace YooAsset
if (_loadBundleOp == null)
{
var urls = _fileCache.Config.RemoteService.GetRemoteUrls(_options.Bundle.GetFileName());
var options = new LoadWebAssetBundleOptions(
var options = new LoadWebArchiveBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: urls,
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor,
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);
if (_options.Bundle.IsEncrypted)
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
else
_loadBundleOp = new LoadWebNormalAssetBundleOperation(options);
_loadBundleOp = new LoadWebArchiveBundleOperation(options);
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);
}
@@ -94,4 +83,4 @@ namespace YooAsset
}
}
}
}
}

View File

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

View File

@@ -1,9 +1,10 @@
namespace YooAsset
{
/// <summary>
/// WebGL 游戏平台加载 AssetBundle 操作
/// WebGL 平台网络缓存加载 AssetBundle 操作
/// </summary>
internal sealed class WGBCLoadAssetBundleOperation : BCLoadBundleOperation
internal sealed class WNBCLoadAssetBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
@@ -12,12 +13,12 @@ namespace YooAsset
Done,
}
private readonly WebGameBundleCache _fileCache;
private readonly WebNetworkBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private ESteps _steps = ESteps.None;
internal WGBCLoadAssetBundleOperation(WebGameBundleCache fileCache, BCLoadBundleOptions options)
internal WNBCLoadAssetBundleOperation(WebNetworkBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
@@ -38,29 +39,30 @@ namespace YooAsset
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: 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: urls,
gamePlatform: _fileCache.Config.GamePlatform,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
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();
@@ -98,4 +100,4 @@ namespace YooAsset
}
}
}
}
}

View File

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

View File

@@ -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
}
}
}
}
}

View File

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

View File

@@ -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;
@@ -87,16 +97,9 @@ namespace YooAsset
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;
}
@@ -108,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 />
@@ -134,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;
}
@@ -152,8 +165,8 @@ namespace YooAsset
/// <inheritdoc />
public string GetCacheFilePath(string bundleGuid)
{
YooLogger.LogWarning($"{nameof(WebRemoteBundleCache)} does not support local cache file path.");
YooLogger.LogWarning($"{nameof(WebNetworkBundleCache)} does not support local cache file path.");
return null;
}
}
}
}

View File

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

View File

@@ -1,27 +0,0 @@
namespace YooAsset
{
/// <summary>
/// Web远端文件缓存初始化操作
/// </summary>
internal sealed class WRBCInitializeOperation : BCInitializeOperation
{
private readonly WebRemoteBundleCache _fileCache;
/// <summary>
/// 创建 Web 远端缓存初始化操作实例
/// </summary>
/// <param name="fileCache">Web 远端文件缓存系统</param>
public WRBCInitializeOperation(WebRemoteBundleCache fileCache)
{
_fileCache = fileCache;
}
protected override void InternalStart()
{
SetResult();
}
protected override void InternalUpdate()
{
}
}
}

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,101 @@
namespace YooAsset
{
/// <summary>
/// Web服务器文件缓存加载 ArchiveBundle 操作
/// </summary>
internal sealed class WSBCLoadArchiveBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
GetEntry,
LoadBundle,
Done,
}
private readonly WebServerBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private WebServerBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None;
public WSBCLoadArchiveBundleOperation(WebServerBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.GetEntry;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetEntry)
{
_cacheEntry = _fileCache.GetEntry(_options.Bundle.BundleGuid);
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)
{
string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath);
var options = new LoadWebArchiveBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: new[] { url },
archiveBundleDecryptor: _fileCache.Config.ArchiveBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
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);
}
}
}
}

View File

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

View File

@@ -58,22 +58,33 @@ namespace YooAsset
if (_loadBundleOp == null)
{
string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath);
var options = new LoadWebAssetBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: new[] { url },
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
if (_options.Bundle.IsEncrypted)
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
{
var encryptedOptions = new LoadWebEncryptedAssetBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: new[] { url },
assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebEncryptedAssetBundleOperation(encryptedOptions);
}
else
_loadBundleOp = new LoadWebNormalAssetBundleOperation(options);
{
var platformOptions = new LoadWebPlatformAssetBundleOptions(
bundle: _options.Bundle,
candidateUrls: new[] { url },
platformStrategy: _fileCache.Config.PlatformStrategy,
downloadBackend: _fileCache.Config.DownloadBackend,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebPlatformAssetBundleOperation(platformOptions);
}
_loadBundleOp.StartOperation();
AddChildOperation(_loadBundleOp);

View File

@@ -0,0 +1,101 @@
namespace YooAsset
{
/// <summary>
/// Web服务器文件缓存加载 RawBundle 操作
/// </summary>
internal sealed class WSBCLoadRawBundleOperation : BCLoadBundleOperation
{
private enum ESteps
{
None,
GetEntry,
LoadBundle,
Done,
}
private readonly WebServerBundleCache _fileCache;
private readonly BCLoadBundleOptions _options;
private BCLoadBundleOperation _loadBundleOp;
private WebServerBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None;
public WSBCLoadRawBundleOperation(WebServerBundleCache fileCache, BCLoadBundleOptions options)
{
_fileCache = fileCache;
_options = options;
}
protected override void InternalStart()
{
_steps = ESteps.GetEntry;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.GetEntry)
{
_cacheEntry = _fileCache.GetEntry(_options.Bundle.BundleGuid);
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)
{
string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath);
var options = new LoadWebRawBundleOptions(
cacheName: _fileCache.GetType().Name,
bundle: _options.Bundle,
candidateUrls: new[] { url },
rawBundleDecryptor: _fileCache.Config.RawBundleDecryptor,
downloadBackend: _fileCache.Config.DownloadBackend,
downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
watchdogTimeout: _fileCache.Config.WatchdogTimeout,
downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
_loadBundleOp = new LoadWebRawBundleOperation(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);
}
}
}
}

View File

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

View File

@@ -30,6 +30,21 @@ namespace YooAsset
/// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary>
/// RawBundle 解密器
/// </summary>
public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary>
/// ArchiveBundle 解密器
/// </summary>
public IBundleDecryptor ArchiveBundleDecryptor { get; }
/// <summary>
/// Web 平台策略
/// </summary>
public IWebPlatformStrategy PlatformStrategy { get; }
/// <summary>
/// 下载后台
/// </summary>
@@ -45,14 +60,18 @@ namespace YooAsset
/// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor,
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor,
IBundleDecryptor archiveBundleDecryptor, IWebPlatformStrategy platformStrategy, IDownloadBackend downloadBackend,
IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadVerifyLevel = downloadVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
ArchiveBundleDecryptor = archiveBundleDecryptor;
PlatformStrategy = platformStrategy;
DownloadBackend = downloadBackend;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
@@ -139,6 +158,16 @@ namespace YooAsset
var operation = new WSBCLoadAssetBundleOperation(this, options);
return operation;
}
else if (options.Bundle.GetBundleType() == (int)EBundleType.RawBundle)
{
var operation = new WSBCLoadRawBundleOperation(this, options);
return operation;
}
else if (options.Bundle.GetBundleType() == (int)EBundleType.ArchiveBundle)
{
var operation = new WSBCLoadArchiveBundleOperation(this, options);
return operation;
}
else
{
string error = $"{nameof(WebServerBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";

View File

@@ -56,15 +56,14 @@ namespace YooAsset
}
private readonly string _archiveFilePath;
private readonly byte[] _memoryData;
private readonly Dictionary<string, FileEntry> _entries;
private readonly Dictionary<string, RawFileObject> _cachedObjects = new Dictionary<string, RawFileObject>();
private bool _isUnloaded;
/// <summary>
/// 创建 ArchiveBundle 实例
/// 从本地文件创建 ArchiveBundle 实例
/// </summary>
/// <param name="archiveFilePath">归档文件的本地路径</param>
/// <param name="entries">子文件索引字典</param>
public ArchiveBundle(string archiveFilePath, Dictionary<string, FileEntry> entries)
{
if (string.IsNullOrEmpty(archiveFilePath))
@@ -73,6 +72,23 @@ namespace YooAsset
throw new ArgumentNullException(nameof(entries));
_archiveFilePath = archiveFilePath;
_memoryData = null;
_entries = entries;
_isUnloaded = false;
}
/// <summary>
/// 从解密后的内存数据创建 ArchiveBundle 实例
/// </summary>
public ArchiveBundle(byte[] memoryData, Dictionary<string, FileEntry> entries)
{
if (memoryData == null)
throw new ArgumentNullException(nameof(memoryData));
if (entries == null)
throw new ArgumentNullException(nameof(entries));
_archiveFilePath = null;
_memoryData = memoryData;
_entries = entries;
_isUnloaded = false;
}
@@ -92,8 +108,8 @@ namespace YooAsset
if (_cachedObjects.TryGetValue(assetPath, out RawFileObject cached))
return cached;
byte[] fileData = ReadFileData(assetPath);
var rawFileObject = RawFileObject.CreateFromBytes(fileData);
byte[] assetData = ReadAssetData(assetPath);
var rawFileObject = RawFileObject.CreateFromBytes(assetData);
_cachedObjects[assetPath] = rawFileObject;
return rawFileObject;
}
@@ -113,14 +129,24 @@ namespace YooAsset
_entries.Clear();
}
/// <summary>
/// 从归档文件中读取子文件的字节数据
/// </summary>
private byte[] ReadFileData(string assetPath)
private byte[] ReadAssetData(string assetPath)
{
if (_entries.TryGetValue(assetPath, out FileEntry entry) == false)
throw new InvalidOperationException($"Asset not found in archive: '{assetPath}'.");
if (_memoryData != null)
return ReadFromMemory(entry);
else
return ReadFromFile(entry);
}
private byte[] ReadFromMemory(FileEntry entry)
{
byte[] buffer = new byte[entry.DataLength];
Buffer.BlockCopy(_memoryData, (int)entry.DataOffset, buffer, 0, (int)entry.DataLength);
return buffer;
}
private byte[] ReadFromFile(FileEntry entry)
{
byte[] buffer = new byte[entry.DataLength];
using (var fs = new FileStream(_archiveFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
@@ -130,7 +156,7 @@ namespace YooAsset
{
int read = fs.Read(buffer, bytesRead, buffer.Length - bytesRead);
if (read == 0)
throw new EndOfStreamException($"Unexpected end of archive file while reading '{assetPath}'.");
throw new EndOfStreamException($"Unexpected end of archive file while reading '{entry.AssetPath}'.");
bytesRead += read;
}
}

View File

@@ -8,14 +8,36 @@ namespace YooAsset
internal static class ArchiveBundleHelper
{
/// <summary>
/// 解析 YARK 归档文件
/// 从本地文件解析 YARK 归档
/// </summary>
/// <param name="filePath">归档文件路径</param>
/// <returns>解析成功的 ArchiveBundle 实例</returns>
public static ArchiveBundle LoadArchiveBundle(string filePath)
public static ArchiveBundle LoadFromFile(string filePath)
{
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
using (var reader = new BinaryReader(fs))
{
var entries = ParseEntries(fs, fs.Length);
return new ArchiveBundle(filePath, entries);
}
}
/// <summary>
/// 从解密后的内存数据解析 YARK 归档
/// </summary>
/// <param name="fileData">解密后的完整归档字节数据</param>
/// <returns>解析成功的 ArchiveBundle 实例</returns>
public static ArchiveBundle LoadFromMemory(byte[] fileData)
{
using (var ms = new MemoryStream(fileData, false))
{
var entries = ParseEntries(ms, fileData.Length);
return new ArchiveBundle(fileData, entries);
}
}
private static Dictionary<string, ArchiveBundle.FileEntry> ParseEntries(Stream stream, long dataLength)
{
using (var reader = new BinaryReader(stream, Encoding.UTF8, true))
{
// 校验文件头魔数YARK
uint magic = reader.ReadUInt32();
@@ -34,7 +56,6 @@ namespace YooAsset
if (fileCount > ArchiveBundleConsts.MaxChildFileCount)
throw new InvalidOperationException($"Archive child file count {fileCount} exceeds maximum ({ArchiveBundleConsts.MaxChildFileCount}).");
long fileLength = fs.Length;
var entries = new Dictionary<string, ArchiveBundle.FileEntry>(fileCount);
for (int i = 0; i < fileCount; i++)
{
@@ -44,9 +65,9 @@ namespace YooAsset
throw new InvalidOperationException($"Invalid path length {pathLen} at entry index {i}.");
if (pathLen > ArchiveBundleConsts.MaxChildFilePathBytes)
throw new InvalidOperationException($"Path length {pathLen} exceeds maximum ({ArchiveBundleConsts.MaxChildFilePathBytes}) at entry index {i}.");
long remaining = fileLength - fs.Position;
long remaining = dataLength - stream.Position;
if (pathLen > remaining)
throw new InvalidOperationException($"Path length {pathLen} exceeds remaining file size at entry index {i}.");
throw new InvalidOperationException($"Path length {pathLen} exceeds remaining data size at entry index {i}.");
string assetPath = Encoding.UTF8.GetString(reader.ReadBytes(pathLen));
if (string.IsNullOrEmpty(assetPath))
@@ -59,15 +80,15 @@ namespace YooAsset
uint crc = reader.ReadUInt32();
// 校验数据范围是否越过文件边界
if (offset < 0 || offset > fileLength)
if (offset < 0 || offset > dataLength)
throw new InvalidOperationException($"Invalid data offset {offset} for '{assetPath}'.");
if (length < 0 || length > fileLength - offset)
throw new InvalidOperationException($"Data range [{offset}, {offset + length}) exceeds file size {fileLength} for '{assetPath}'.");
if (length < 0 || length > dataLength - offset)
throw new InvalidOperationException($"Data range [{offset}, {offset + length}) exceeds data size {dataLength} for '{assetPath}'.");
entries[assetPath] = new ArchiveBundle.FileEntry(assetPath, offset, length, crc);
}
return new ArchiveBundle(filePath, entries);
return entries;
}
}
}

View File

@@ -0,0 +1,124 @@
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// InstantAsset 表上下文
/// </summary>
internal sealed class InstantAssetTableContext
{
/// <summary>
/// 初始化结果
/// </summary>
public readonly struct InitializeResult
{
/// <summary>
/// 错误信息
/// </summary>
public readonly string Error;
/// <summary>
/// 初始化是否成功
/// </summary>
public bool Succeeded
{
get { return Error == null; }
}
private InitializeResult(string error)
{
Error = error;
}
/// <summary>
/// 创建表示初始化成功的默认结果
/// </summary>
public static InitializeResult Default()
{
return new InitializeResult(null);
}
/// <summary>
/// 创建表示初始化失败的结果
/// </summary>
/// <param name="error">错误信息</param>
public static InitializeResult Failure(string error)
{
return new InitializeResult(error);
}
}
private readonly string _rootPath;
private readonly string _assetTableName;
private readonly string _sceneTableName;
/// <summary>
/// 资源表
/// </summary>
public InstantAssetTable AssetTable { get; private set; }
/// <summary>
/// 场景表
/// </summary>
/// <remarks>
/// 如果不包含场景资源,该值为空值。
/// </remarks>
public InstantAssetTable SceneTable { get; private set; }
public InstantAssetTableContext(string rootPath, string assetTableName)
{
_rootPath = rootPath;
_assetTableName = assetTableName;
_sceneTableName = $"{assetTableName}-scene";
}
/// <summary>
/// 初始化表上下文
/// </summary>
/// <returns>初始化结果</returns>
public InitializeResult Initialize()
{
InstantAsset.SetInstantAssetRootPath(_rootPath);
string assetTablePath = PathUtility.Combine(_rootPath, _assetTableName);
AssetTable = InstantAsset.ReadAssetTable(assetTablePath) as InstantAssetTable;
if (AssetTable == null)
{
string error = $"Failed to load InstantAssetTable: '{assetTablePath}'.";
return InitializeResult.Failure(error);
}
string sceneTablePath = PathUtility.Combine(_rootPath, _sceneTableName);
SceneTable = InstantAsset.ReadAssetTable(sceneTablePath) as InstantAssetTable;
if (SceneTable == null)
{
YooLogger.LogWarning($"InstantAsset scene table not found: '{sceneTablePath}'.");
}
return InitializeResult.Default();
}
/// <summary>
/// 卸载当前上下文持有的所有表
/// </summary>
public void Dispose()
{
if (AssetTable != null)
{
string assetTablePath = PathUtility.Combine(_rootPath, _assetTableName);
InstantAsset.UnloadAssetTable(assetTablePath);
AssetTable = null;
}
if (SceneTable != null)
{
string sceneTablePath = PathUtility.Combine(_rootPath, _sceneTableName);
InstantAsset.UnloadAssetTable(sceneTablePath);
SceneTable = null;
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,57 @@
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
/// <summary>
/// InstantAsset 资源包句柄
/// </summary>
internal sealed class InstantBundleHandle : IBundleHandle
{
private readonly PackageBundle _packageBundle;
private readonly InstantAssetTable _assetTable;
private readonly InstantAssetTable _sceneTable;
public InstantBundleHandle(PackageBundle packageBundle, InstantAssetTable assetTable, InstantAssetTable sceneTable)
{
_packageBundle = packageBundle;
_assetTable = assetTable;
_sceneTable = sceneTable;
}
/// <inheritdoc/>
public void UnloadBundle()
{
}
/// <inheritdoc/>
public BHLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo)
{
var operation = new IBHLoadAssetOperation(_packageBundle, _assetTable, assetInfo);
return operation;
}
/// <inheritdoc/>
public BHLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo)
{
var operation = new IBHLoadAllAssetsOperation();
return operation;
}
/// <inheritdoc/>
public BHLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo)
{
var operation = new IBHLoadSubAssetsOperation();
return operation;
}
/// <inheritdoc/>
public BHLoadSceneOperation LoadSceneAsync(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation)
{
var operation = new IBHLoadSceneOperation(_sceneTable, assetInfo, loadSceneParams, allowSceneActivation);
return operation;
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,18 @@
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
namespace YooAsset
{
/// <summary>
/// 全部资源加载操作InstantBundle不支持
/// </summary>
internal sealed class IBHLoadAllAssetsOperation : BHLoadAllAssetsOperation
{
protected override void InternalStart()
{
SetError($"{nameof(IBHLoadAllAssetsOperation)} does not support loading all assets.");
}
protected override void InternalUpdate()
{
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,119 @@
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 单个资源加载操作InstantBundle
/// </summary>
internal sealed class IBHLoadAssetOperation : BHLoadAssetOperation
{
private enum ESteps
{
None,
CheckAssetTable,
LoadAsset,
CheckResult,
Done,
}
private readonly PackageBundle _packageBundle;
private readonly InstantAssetTable _assetTable;
private readonly AssetInfo _assetInfo;
private InstantAssetRequest _request;
private ESteps _steps = ESteps.None;
public IBHLoadAssetOperation(PackageBundle packageBundle, InstantAssetTable assetTable, AssetInfo assetInfo)
{
_packageBundle = packageBundle;
_assetTable = assetTable;
_assetInfo = assetInfo;
}
protected override void InternalStart()
{
_steps = ESteps.CheckAssetTable;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckAssetTable)
{
if (_assetTable == null)
{
_steps = ESteps.Done;
SetError($"{nameof(IBHLoadAssetOperation)} asset table is null, cannot load asset: '{_assetInfo.AssetPath}'.");
YooLogger.LogError(Error);
return;
}
_steps = ESteps.LoadAsset;
}
if (_steps == ESteps.LoadAsset)
{
if (IsWaitForCompletion)
{
if (_assetInfo.AssetType == null)
Result = _assetTable.LoadAsset(_assetInfo.AssetPath);
else
Result = _assetTable.LoadAsset(_assetInfo.AssetPath, _assetInfo.AssetType);
}
else
{
if (_assetInfo.AssetType == null)
_request = _assetTable.LoadAssetAsync(_assetInfo.AssetPath);
else
_request = _assetTable.LoadAssetAsync(_assetInfo.AssetPath, _assetInfo.AssetType);
}
_steps = ESteps.CheckResult;
}
if (_steps == ESteps.CheckResult)
{
if (_request != null)
{
// 注意: 异步加载过程中,业务逻辑可能会强制转换为同步加载
if (IsWaitForCompletion)
{
// 强制挂起主线程(注意:该操作会很耗时)
YooLogger.LogWarning("Blocking the main thread while loading an InstantAsset.");
Result = _request.asset;
}
else
{
Progress = _request.progress;
if (_request.isDone == false)
return;
Result = _request.asset;
}
}
if (Result == null)
{
string error;
if (_assetInfo.AssetType == null)
error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: null InstantBundle: '{_packageBundle.BundleName}'.";
else
error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType} InstantBundle: '{_packageBundle.BundleName}'.";
_steps = ESteps.Done;
SetError(error);
YooLogger.LogError(Error);
}
else
{
_steps = ESteps.Done;
SetResult();
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,130 @@
#if TUANJIE_1_8_OR_NEWER && YOOASSET_INSTANT_ASSET_SUPPORT
using UnityEngine;
using UnityEngine.SceneManagement;
namespace YooAsset
{
/// <summary>
/// 场景加载操作InstantBundle
/// </summary>
internal sealed class IBHLoadSceneOperation : BHLoadSceneOperation
{
private enum ESteps
{
None,
CheckSceneTable,
LoadScene,
CheckResult,
Done,
}
private readonly InstantAssetTable _sceneTable;
private readonly AssetInfo _assetInfo;
private readonly LoadSceneParameters _loadSceneParams;
private bool _allowSceneActivation;
private AsyncOperation _asyncOperation;
private ESteps _steps = ESteps.None;
public IBHLoadSceneOperation(InstantAssetTable sceneTable, AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation)
{
_sceneTable = sceneTable;
_assetInfo = assetInfo;
_loadSceneParams = loadSceneParams;
_allowSceneActivation = allowSceneActivation;
}
protected override void InternalStart()
{
_steps = ESteps.CheckSceneTable;
}
protected override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.CheckSceneTable)
{
if (_sceneTable == null)
{
_steps = ESteps.Done;
SetError($"{nameof(IBHLoadSceneOperation)} scene table is null, cannot load scene: '{_assetInfo.AssetPath}'.");
YooLogger.LogError(Error);
return;
}
_steps = ESteps.LoadScene;
}
if (_steps == ESteps.LoadScene)
{
if (IsWaitForCompletion)
{
_steps = ESteps.Done;
SetError($"{nameof(IBHLoadSceneOperation)} does not support synchronous scene loading.");
YooLogger.LogError(Error);
return;
}
else
{
_asyncOperation = SceneManager.LoadSceneAsync(_assetInfo.AssetPath, _loadSceneParams);
if (_asyncOperation != null)
{
_asyncOperation.allowSceneActivation = _allowSceneActivation;
_asyncOperation.priority = 100;
Result = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
_steps = ESteps.CheckResult;
}
else
{
_steps = ESteps.Done;
SetError($"Failed to load scene: '{_assetInfo.AssetPath}'.");
YooLogger.LogError(Error);
}
}
}
if (_steps == ESteps.CheckResult)
{
if (_asyncOperation != null)
{
if (IsWaitForCompletion)
{
YooLogger.LogError("The scene is already loading asynchronously.");
}
else
{
if (_asyncOperation.allowSceneActivation == false)
{
if (_allowSceneActivation)
_asyncOperation.allowSceneActivation = true;
}
Progress = _asyncOperation.progress;
if (_asyncOperation.isDone == false)
return;
}
}
if (Result.IsValid())
{
_steps = ESteps.Done;
SetResult();
}
else
{
_steps = ESteps.Done;
SetError($"Loaded scene is invalid: '{_assetInfo.AssetPath}'.");
YooLogger.LogError(Error);
}
}
}
protected override void InternalWaitForCompletion()
{
ExecuteOnce();
}
protected override void InternalAllowSceneActivation()
{
_allowSceneActivation = true;
}
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More