refactor : 代码重构

This commit is contained in:
何冠峰
2026-03-26 21:02:11 +08:00
parent 8b35472991
commit d47b9e9d56
304 changed files with 4305 additions and 2618 deletions

View File

@@ -53,6 +53,8 @@
| `LogOff` | `logOff` | ~~LogOut~~ | | `LogOff` | `logOff` | ~~LogOut~~ |
| `Email` | `email` | ~~EMail~~ | | `Email` | `email` | ~~EMail~~ |
> ✔️ 在 Unity 项目中,`ID` 保持全大写以与 Unity API 风格一致(如 `GetInstanceID()`、`PropertyToID()`),项目内应保持风格统一。
### 1.5 区分大小写 ### 1.5 区分大小写
- 不要假定所有语言都区分大小写 - 不要假定所有语言都区分大小写

View File

@@ -14,12 +14,12 @@
```csharp ```csharp
// ✔️ // ✔️
$"File cache entry not found: '{bundleGUID}'." $"File cache entry not found: '{bundleGuid}'."
$"Invalid clear method: '{options.ClearMethod}'." $"Invalid clear method: '{options.ClearMethod}'."
$"Could not find file '{filePath}'." $"Could not find file '{filePath}'."
// ❌ // ❌
$"File cache entry not found: {bundleGUID}." $"File cache entry not found: {bundleGuid}."
``` ```
### 1.2 数值变量不加引号 ### 1.2 数值变量不加引号
@@ -95,6 +95,17 @@ throw new Exception("catalog file data is null or empty");
"Bundle handle null." "Bundle handle null."
``` ```
> **例外**`not found` 等 .NET 生态中广泛使用的惯用短语,允许省略 `was`,以保持简洁和一致性。
```csharp
// ✔️ 惯用短语,省略 was
"File cache entry not found: '{bundleGuid}'."
"Asset not found: '{assetPath}'."
// ❌ 冗余
"File cache entry was not found: '{bundleGuid}'."
```
### 2.3 不要以冠词或变量开头 ### 2.3 不要以冠词或变量开头
消息以关键实体词开头,不要以 `The` / `A` / `An` 或变量开头,便于日志搜索和排序。 消息以关键实体词开头,不要以 `The` / `A` / `An` 或变量开头,便于日志搜索和排序。
@@ -103,11 +114,11 @@ throw new Exception("catalog file data is null or empty");
```csharp ```csharp
// ✔️ // ✔️
$"Cache entry already exists: '{bundleGUID}'." $"Cache entry already exists: '{bundleGuid}'."
$"Log file '{name}' is full." $"Log file '{name}' is full."
// ❌ // ❌
$"The cache entry already exists: '{bundleGUID}'." $"The cache entry already exists: '{bundleGuid}'."
$"'{name}' log file is full." $"'{name}' log file is full."
``` ```
@@ -174,7 +185,7 @@ $"Exception in {this.GetType().Name}.InternalStart: {ex}."
```csharp ```csharp
// ✔️ 统一格式 // ✔️ 统一格式
$"File cache entry not found: '{bundleGUID}'." // 所有缓存未命中统一使用此句式 $"File cache entry not found: '{bundleGuid}'." // 所有缓存未命中统一使用此句式
$"Exception in {GetType().Name}.{MethodName}: {ex}." // 所有异常日志统一使用此句式 $"Exception in {GetType().Name}.{MethodName}: {ex}." // 所有异常日志统一使用此句式
// ❌ 同一语义不同表达 // ❌ 同一语义不同表达
@@ -199,7 +210,7 @@ $"Exception in {GetType().Name}.{MethodName}: {ex}." // 所有异常日志统一
| # | 规则 | 正确示例 | 错误示例 | | # | 规则 | 正确示例 | 错误示例 |
|---|------|---------|---------| |---|------|---------|---------|
| 1 | 字符串变量值加单引号 | `'{bundleGUID}'` | `{bundleGUID}` | | 1 | 字符串变量值加单引号 | `'{bundleGuid}'` | `{bundleGuid}` |
| 2 | 数值变量不加引号 | `{count}` | `'{count}'` | | 2 | 数值变量不加引号 | `{count}` | `'{count}'` |
| 3 | 类型名作主语不加引号 | `TypeName is not ...` | `'TypeName' is not ...` | | 3 | 类型名作主语不加引号 | `TypeName is not ...` | `'TypeName' is not ...` |
| 4 | 异常对象不加引号 | `{ex.Message}` | `'{ex.Message}'` | | 4 | 异常对象不加引号 | `{ex.Message}` | `'{ex.Message}'` |

View File

@@ -21,7 +21,6 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
| `<summary>` | 简要描述类型或成员的功能 | 所有公共类型和成员 | | `<summary>` | 简要描述类型或成员的功能 | 所有公共类型和成员 |
| `<param>` | 描述方法参数 | 方法、构造函数 | | `<param>` | 描述方法参数 | 方法、构造函数 |
| `<returns>` | 描述方法返回值 | 有返回值的方法 | | `<returns>` | 描述方法返回值 | 有返回值的方法 |
| `<exception>` | 说明可能抛出的异常 | 可能抛出异常的方法 |
| `<remarks>` | 补充说明,提供额外细节 | 需要详细解释的成员 | | `<remarks>` | 补充说明,提供额外细节 | 需要详细解释的成员 |
| `<value>` | 描述属性所代表的值 | 属性 | | `<value>` | 描述属性所代表的值 | 属性 |
@@ -41,7 +40,6 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
| `<para>` | 在 `<remarks>` 等标签内分段 | | `<para>` | 在 `<remarks>` 等标签内分段 |
| `<list>` | 创建列表或表格 | | `<list>` | 创建列表或表格 |
| `<code>` | 多行代码示例 | | `<code>` | 多行代码示例 |
| `<c>` | 行内代码片段 |
| `<example>` | 包含使用示例 | | `<example>` | 包含使用示例 |
### 2.4 泛型与继承标签 ### 2.4 泛型与继承标签
@@ -71,13 +69,15 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
|----------|---------|---------| |----------|---------|---------|
| 类 / 结构 | `<summary>` | `<remarks>``<typeparam>` | | 类 / 结构 | `<summary>` | `<remarks>``<typeparam>` |
| 接口 | `<summary>` | `<remarks>` | | 接口 | `<summary>` | `<remarks>` |
| 方法 | `<summary>``<param>``<returns>` | `<exception>``<remarks>``<example>` | | 方法 | `<summary>``<param>``<returns>` | `<remarks>``<example>` |
| 构造函数 | `<summary>``<param>` | `<exception>` | | 构造函数 | `<summary>``<param>` | |
| 属性 | `<summary>` | `<value>``<exception>` | | 属性 | `<summary>` | `<value>` |
| 事件 | `<summary>` | `<remarks>` | | 事件 | `<summary>` | `<remarks>` |
| 枚举类型 | `<summary>` | — | | 枚举类型 | `<summary>` | — |
| 枚举值 | `<summary>` | — | | 枚举值 | `<summary>` | — |
| 委托 | `<summary>``<param>``<returns>` | — | | 委托 | `<summary>``<param>``<returns>` | — |
| 接口实现成员 | `<inheritdoc/>` | — |
| override 方法 | **不添加**注释 | — |
--- ---
@@ -89,17 +89,46 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
✔️ 以**第三人称动词**开头。 ✔️ 以**第三人称动词**开头。
✔️ 完整语句末尾**建议**使用句号,短语式描述可省略 ✔️ 句号规则:注释内容含逗号、分号等分句标点时,末尾**加**句号;纯短语**不加**句号
```csharp
// ✔️ 含逗号 → 加句号
/// <param name="timeout">超时时间0 表示不限制。</param>
/// <returns>下载结果,包含状态码和错误信息。</returns>
// ✔️ 纯短语 → 不加句号
/// <param name="url">资源包的完整下载地址</param>
/// <returns>初始化操作句柄</returns>
```
❌ **禁止**重复方法名或参数类型中已表达的信息。 ❌ **禁止**重复方法名或参数类型中已表达的信息。
❌ **禁止**在注释中重复方法命名后缀已表达的语义。常见后缀包括:
| 命名后缀 | 已表达的语义 | 注释中禁止使用 |
|----------|------------|--------------|
| `Async` | 异步执行 | "异步" |
| `Internal` | 内部实现 | "内部" |
❌ **禁止**以"这个方法"、"该类"等冗余前缀开头。 ❌ **禁止**以"这个方法"、"该类"等冗余前缀开头。
### 4.2 常见动词约定 ### 4.2 常见开头约定
#### 属性 —— 描述值本身,不加动词前缀
属性的 `{ get; }` / `{ get; set; }` 访问器已表达读写语义summary 应直接描述**值的含义**,避免用"获取"、"获取或设置"等动词前缀重复已知信息。
| 属性类型 | 推荐写法 | 反例 |
|----------|---------|------|
| 只读属性 | 名词短语,如 "错误信息" | ~~"获取错误信息"~~ |
| 可读写属性 | 名词短语,如 "当前下载进度" | ~~"获取或设置当前下载进度"~~ |
| 返回 bool 的属性 | "是否……",如 "是否为只读缓存" | ~~"判断是否为只读缓存"~~ |
#### 方法 —— 以动词开头
| 成员类型 | 推荐开头动词 | | 成员类型 | 推荐开头动词 |
|----------|-------------| |----------|-------------|
| 返回 bool 的方法 / 属性 | "检查是否"、"判断是否" | | 返回 bool 的方法 | "检查是否"、"判断是否" |
| 获取类方法 | "获取"、"查询" | | 获取类方法 | "获取"、"查询" |
| 设置类方法 | "设置"、"更新" | | 设置类方法 | "设置"、"更新" |
| 创建类方法 | "创建"、"构建"、"生成" | | 创建类方法 | "创建"、"构建"、"生成" |
@@ -112,8 +141,6 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
✔️ 说明有效范围和边界值(如为 `null` 时的行为、取值范围)。 ✔️ 说明有效范围和边界值(如为 `null` 时的行为、取值范围)。
✔️ 完整语句末尾**建议**使用句号,短语式描述可省略。
❌ **禁止**仅重复参数名或类型名,如"url 参数"。 ❌ **禁止**仅重复参数名或类型名,如"url 参数"。
### 4.4 `<returns>` 书写规范 ### 4.4 `<returns>` 书写规范
@@ -122,13 +149,25 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
✔️ 说明**特殊返回值**的含义(如返回 `null` 表示未找到)。 ✔️ 说明**特殊返回值**的含义(如返回 `null` 表示未找到)。
### 4.5 `<exception>` 书写规范 ### 4.5 `<remarks>` 书写规范
✔️ 指定异常的**具体类型**`cref` 属性) ✔️ 多行内容**必须**用 `<para>` 包裹每段,否则 IDE 会将多行合并显示为一行
✔️ 说明异常的**触发条件**。 ```csharp
// ✔️ 好:每段用 <para> 包裹
/// <remarks>
/// <para>下载并加载 Unity AssetBundle 资源包</para>
/// <para>支持 Unity 内置缓存机制和 CRC 校验</para>
/// </remarks>
✔️ 使用 `<paramref>` 引用相关参数。 // ❌ 差多行纯文本IDE 显示时会合并为一行
/// <remarks>
/// 下载并加载 Unity AssetBundle 资源包
/// 支持 Unity 内置缓存机制和 CRC 校验
/// </remarks>
```
✔️ 单行内容可直接书写,无需 `<para>`
--- ---
@@ -141,8 +180,8 @@ C# 使用 XML 文档注释为代码提供结构化文档。编译器从 `///`
/// 资源包下载请求,负责管理单个资源包的下载生命周期。 /// 资源包下载请求,负责管理单个资源包的下载生命周期。
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>支持断点续传和失败重试</para> /// <para>支持断点续传和失败重试</para>
/// <para>通过 <see cref="DownloadRetryController"/> 控制重试策略</para> /// <para>通过 <see cref="DownloadRetryController"/> 控制重试策略</para>
/// </remarks> /// </remarks>
public class BundleDownloadRequest public class BundleDownloadRequest
{ {
@@ -153,16 +192,12 @@ public class BundleDownloadRequest
```csharp ```csharp
/// <summary> /// <summary>
/// 从指定 URL 异步下载资源包并保存到本地磁盘 /// 从指定 URL 下载资源包并保存到本地磁盘
/// </summary> /// </summary>
/// <param name="url">资源包的完整下载地址</param> /// <param name="url">资源包的完整下载地址</param>
/// <param name="savePath">本地保存的目标路径</param> /// <param name="savePath">本地保存的目标路径</param>
/// <param name="timeout">超时时间0 表示不限制。</param> /// <param name="timeout">超时时间0 表示不限制。</param>
/// <returns>下载结果,包含状态码和错误信息。</returns> /// <returns>下载结果,包含状态码和错误信息。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="url"/> 或 <paramref name="savePath"/> 为 <c>null</c> 时抛出。
/// </exception>
/// <exception cref="TimeoutException">当下载超过指定时间时抛出。</exception>
public async Task<DownloadResult> DownloadAsync(string url, string savePath, int timeout = 30) public async Task<DownloadResult> DownloadAsync(string url, string savePath, int timeout = 30)
{ {
} }
@@ -172,7 +207,7 @@ public async Task<DownloadResult> DownloadAsync(string url, string savePath, int
```csharp ```csharp
/// <summary> /// <summary>
/// 获取当前下载进度 /// 当前下载进度
/// </summary> /// </summary>
/// <value>取值范围 0.0 ~ 1.0,其中 1.0 表示下载完成。</value> /// <value>取值范围 0.0 ~ 1.0,其中 1.0 表示下载完成。</value>
public float Progress { get; private set; } public float Progress { get; private set; }
@@ -182,7 +217,7 @@ public float Progress { get; private set; }
```csharp ```csharp
/// <summary> /// <summary>
/// 下载任务的运行状态 /// 下载任务的运行状态
/// </summary> /// </summary>
public enum DownloadStatus public enum DownloadStatus
{ {
@@ -192,17 +227,17 @@ public enum DownloadStatus
Pending, Pending,
/// <summary> /// <summary>
/// 正在下载中 /// 正在下载中
/// </summary> /// </summary>
Downloading, Downloading,
/// <summary> /// <summary>
/// 下载已完成 /// 下载已完成
/// </summary> /// </summary>
Completed, Completed,
/// <summary> /// <summary>
/// 下载失败 /// 下载失败
/// </summary> /// </summary>
Failed Failed
} }
@@ -224,17 +259,17 @@ public class ObjectPool<T> where T : IDisposable
```csharp ```csharp
/// <summary> /// <summary>
/// 定义下载请求的标准行为 /// 定义下载请求的标准行为
/// </summary> /// </summary>
public interface IDownloadRequest public interface IDownloadRequest
{ {
/// <summary> /// <summary>
/// 获取请求的远程 URL /// 请求的远程 URL
/// </summary> /// </summary>
string URL { get; } string URL { get; }
/// <summary> /// <summary>
/// 取消当前下载请求 /// 取消当前下载请求
/// </summary> /// </summary>
void Abort(); void Abort();
} }
@@ -242,6 +277,10 @@ public interface IDownloadRequest
### 5.7 使用 `<inheritdoc/>` ### 5.7 使用 `<inheritdoc/>`
#### 接口实现 —— 使用 `<inheritdoc/>`
实现接口成员时,使用 `<inheritdoc/>` 继承接口中定义的文档:
```csharp ```csharp
public class MyDownloadRequest : IDownloadRequest public class MyDownloadRequest : IDownloadRequest
{ {
@@ -255,14 +294,35 @@ public class MyDownloadRequest : IDownloadRequest
} }
``` ```
#### 基类继承override —— 不添加任何注释
重写基类方法时,**不需要**添加任何 XML 文档注释(包括 `<inheritdoc/>`)。方法签名中的 `override` 关键字已明确表达继承关系IDE 会自动展示基类文档。
```csharp
// ✔️ 好override 方法不添加注释
protected override void InternalStart()
{
}
protected override void InternalUpdate()
{
}
// ❌ 差:多余的 inheritdoc
/// <inheritdoc/>
protected override void InternalStart()
{
}
```
### 5.8 使用 `<example>` 和 `<code>` ### 5.8 使用 `<example>` 和 `<code>`
```csharp ```csharp
/// <summary> /// <summary>
/// 根据资源路径异步加载资源对象 /// 根据资源路径加载资源对象
/// </summary> /// </summary>
/// <param name="assetPath">资源路径</param> /// <param name="assetPath">资源路径</param>
/// <returns>资源操作句柄,加载失败时资源对象为 <c>null</c>。</returns> /// <returns>资源操作句柄,加载失败时资源对象为 null。</returns>
/// <example> /// <example>
/// 加载一个预制体: /// 加载一个预制体:
/// <code> /// <code>
@@ -291,10 +351,10 @@ public void Download(string url, string savePath) { }
// ✔️ 好:说明行为和目的 // ✔️ 好:说明行为和目的
/// <summary> /// <summary>
/// 从远程服务器下载资源包并保存到本地磁盘 /// 从远程服务器下载资源包并保存到本地磁盘
/// </summary> /// </summary>
/// <param name="url">资源包的完整下载地址</param> /// <param name="url">资源包的完整下载地址</param>
/// <param name="savePath">本地保存的目标路径</param> /// <param name="savePath">本地保存的目标路径</param>
public void Download(string url, string savePath) { } public void Download(string url, string savePath) { }
``` ```
@@ -302,20 +362,36 @@ public void Download(string url, string savePath) { }
```csharp ```csharp
// ❌ 差:仅重复参数名 // ❌ 差:仅重复参数名
/// <param name="name">名称</param> /// <param name="name">名称</param>
// ✔️ 好:说明含义和约束 // ✔️ 好:说明含义和约束
/// <param name="name">资源包的唯一标识名称,不可为 <c>null</c> 或空字符串。</param> /// <param name="name">资源包的唯一标识名称</param>
``` ```
### 6.3 缺少边界说明 ### 6.3 重复命名后缀
```csharp
// ❌ 差:方法名 Async 后缀已表达异步语义
/// <summary>
/// 异步加载资源对象
/// </summary>
public AssetHandle LoadAssetAsync(string location) { }
// ✔️ 好:描述功能意图,不重复后缀语义
/// <summary>
/// 加载资源对象
/// </summary>
public AssetHandle LoadAssetAsync(string location) { }
```
### 6.4 缺少边界说明
```csharp ```csharp
// ❌ 差:未说明返回 null 的情况 // ❌ 差:未说明返回 null 的情况
/// <returns>资源对象</returns> /// <returns>资源对象</returns>
// ✔️ 好:说明特殊返回值 // ✔️ 好:说明特殊返回值
/// <returns>加载到的资源对象,如果资源不存在则返回 <c>null</c>。</returns> /// <returns>加载到的资源对象</returns>
``` ```
--- ---
@@ -351,3 +427,77 @@ if (count > maxCount)
if (count > maxCount) if (count > maxCount)
count = 0; count = 0;
``` ```
---
## 八、术语翻译映射表
> 为保持注释用语统一,以下列出项目中常用英文类型/概念与其中文翻译的对应关系。
> 撰写或审核注释时,请以此表为准。
### 8.1 核心领域类型
| 英文类型 / 概念 | 中文翻译 | 备注 |
|-----------------|---------|------|
| `PackageBundle` | 资源包描述 | 清单中的静态元数据(`Package*` = 描述) |
| `PackageAsset` | 资源描述 | 清单中的静态元数据(`Package*` = 描述) |
| `AssetInfo` | 资源信息 | 运行时上下文(`*Info` = 信息) |
| `BundleInfo` | 资源包信息 | 运行时上下文(`*Info` = 信息) |
| `ResourcePackage` | 资源包裹 | 顶层包裹容器 |
| `PackageManifest` | 包裹清单 / 资源清单 | — |
| `AssetBundle` | AssetBundle | Unity 引擎类型,保留英文 |
| `RawBundle` | 原生资源包 | 非 AssetBundle 的原始文件 |
### 8.2 文件系统与缓存
| 英文类型 / 概念 | 中文翻译 | 备注 |
|-----------------|---------|------|
| `IFileSystem` | 文件系统 | — |
| `IBundleCache` | 缓存系统 | — |
| `ICacheEntry` | 缓存条目 | — |
| `BundleGuid` | 资源包 GUID / Bundle 唯一标识 | — |
| `ICacheEvictionPolicy` | 淘汰策略 | — |
| `EvictionResult` | 淘汰结果 | — |
### 8.3 下载与网络
| 英文类型 / 概念 | 中文翻译 | 备注 |
|-----------------|---------|------|
| `IDownloadRequest` | 下载请求 | — |
| `IDownloadBackend` | 下载后台 | — |
| `IRemoteService` | 远端资源服务 | — |
| `IDownloadRetryPolicy` | 下载重试策略 | — |
| `IDownloadUrlPolicy` | URL 选择策略 | — |
### 8.4 加密与解密
| 英文类型 / 概念 | 中文翻译 | 备注 |
|-----------------|---------|------|
| `IBundleEncryptor` | 资源包加密器 | — |
| `IBundleDecryptor` | 资源包解密器 | 基接口,需实现派生接口 |
| `IManifestEncryptor` | 资源清单加密器 | — |
| `IManifestDecryptor` | 资源清单解密器 | — |
| `BundleEncryptArgs` | 加密操作的输入参数 | — |
| `BundleDecryptArgs` | 解密操作的输入参数 | — |
### 8.5 资源加载与句柄
| 英文类型 / 概念 | 中文翻译 | 备注 |
|-----------------|---------|------|
| `IBundleHandle` | 资源包句柄 | — |
| `AssetHandle` | 资源句柄 | — |
| `RawFileHandle` | 原生文件句柄 | — |
| `SubAssetsHandle` | 子资源句柄 | — |
### 8.6 通用术语
| 英文术语 | 中文翻译 | 备注 |
|---------|---------|------|
| Package | 包裹 | 项目级容器 |
| Bundle | 资源包 | 构建产物单元 |
| Asset | 资源 | 单个资源文件 |
| Manifest | 清单 | — |
| Handle | 句柄 | — |
| Operation | 操作 / 异步操作 | — |
| Scheduler | 调度器 | — |
| Provider | 提供者 | — |

View File

@@ -0,0 +1,200 @@
# 逻辑检测
> **总则**:检查运行时代码的关键逻辑路径,确保宏分支、异步操作生命周期、参数完整性和资源释放均无遗漏。
## 1. 宏包裹代码逻辑
检查所有 `#if` / `#elif` / `#else` / `#endif` 包裹的代码:
- 每个分支的变量赋值路径是否完整(不存在未赋值分支)
- `#else` 兜底分支是否覆盖所有未列出的平台
- 各分支之间的语义是否一致(同一变量在不同分支中的类型和用途是否相同)
- 分支中引用的 API 是否与对应宏版本匹配(如 `UNITY_2020_3_OR_NEWER` 对应的 API
## 2. 子异步操作检查
**定义**:通过 `AddChildOperation()` 添加的操作为子异步操作。
### 2.1 同步等待传播
如果类实现了 `InternalWaitForCompletion()` 方法,所有子异步操作必须在该方法中主动调用 `WaitForCompletion()`
```
❌ 父操作实现了 InternalWaitForCompletion但未对子操作调用 WaitForCompletion
✅ 父操作在 InternalWaitForCompletion 中对每个子操作调用 WaitForCompletion()
```
### 2.2 更新驱动
所有子异步操作必须在父操作的 `InternalUpdate()` 中主动调用 `UpdateOperation()`,确保子操作能被正常驱动。
```
❌ 子操作被 AddChildOperation 添加后,未在 InternalUpdate 中调用 UpdateOperation
✅ 每个子操作在 InternalUpdate 中被调用 UpdateOperation()
```
## 3. 异步操作参数完整性
检查所有 Operation 的 Options 在 `new` 的位置是否完整填充了所有成员字段。
- 找到 Options 结构体的所有字段定义
- 找到所有 `new XxxOptions(...)` 的调用点
- 逐一比对构造参数是否覆盖了所有必填字段
- 可选参数(有默认值)可以跳过
```
❌ new DownloadFileRequestOptions(url, savePath, timeout) // 遗漏了 watchdogTimeout
✅ new DownloadFileRequestOptions(url, savePath, timeout, watchdogTimeout)
```
## 4. 整数溢出与取模安全
检查所有对 `int` 计数器执行 `%`(取模)运算的位置,确保不会因溢出产生负索引。
- 只增不减的 `int` 计数器(如失败计数、轮转索引),溢出 `int.MaxValue` 后变为负数
- 负数 `%` 正数在 C# 中结果为负数,用作数组/列表索引会触发 `ArgumentOutOfRangeException`
- 使用 `& 0x7FFFFFFF` 清除符号位,保证结果始终为非负整数(.NET 中处理哈希取模的惯用手法)
```
❌ int index = _counter % list.Count; // _counter 溢出后 index 为负
✅ int index = (_counter & 0x7FFFFFFF) % list.Count; // 始终非负
```
## 5. 下载请求资源释放
### 5.1 IDownloadRequest 释放
检查所有持有 `IDownloadRequest`(及其子接口)实例的 Operation是否在 `InternalDispose()` 中调用了 `Dispose()` 释放资源。
```
❌ Operation 持有 IDownloadFileRequest 但未在 InternalDispose 中释放
✅ protected override void InternalDispose() { _request?.Dispose(); }
```
### 5.2 下载数据完整性验证
检查文件数据下载完成后,是否有容错或数据完整性验证:
- 是否校验文件大小(下载字节数与预期是否一致)
- 是否校验文件哈希CRC / MD5 / SHA 等)
- 如果无校验,是否有其他容错机制(如重试、回退)
- 无任何验证且无容错机制的,标记为需要关注
## 6. 计时方式一致性
检查所有需要"测量经过时间"的逻辑,确保计时方式不会因帧率波动或 App 后台恢复而失真。
- 使用 `Time.unscaledDeltaTime` 累加计时的方式,在 App 从后台恢复时会产生巨大 spike一帧跨越几十秒导致等待逻辑在单帧内被直接跳过
- 同一系统内应保持计时方式统一,避免一处用绝对时间戳、另一处用帧增量
- 推荐使用 `TimeUtility.RealtimeSinceStartup` 记录起止时间戳做差值判断
```
❌ _elapsed += Time.unscaledDeltaTime; return _elapsed >= delay; // 后台恢复时 spike 跳过等待
✅ return TimeUtility.RealtimeSinceStartup - _startTime >= delay; // 绝对时间戳,不受 spike 影响
```
## 7. 子类回调不得覆写基类已确定的状态
检查所有基类定义的"成功/失败回调"(如 `OnRequestSucceeded``OnRequestFailed`),子类在重写时不应直接修改基类已确定的 `Status`
- 基类在调用回调前已设置 `Status = Succeeded`,若子类在回调中将其改为 `Failed`,后续基类逻辑可能与实际状态不一致
- 回调应仅负责提取结果或设置错误信息,状态转换应由基类统一控制
- 推荐将回调签名改为返回 `bool`(或通过 out 参数传递错误),基类根据返回值决定最终状态
```
❌ protected override void OnRequestSucceeded()
{
if (result == null) Status = Failed; // 子类直接覆写基类已确定的 Succeeded 状态
}
✅ protected override bool OnRequestSucceeded()
{
if (result == null) { Error = "..."; return false; } // 返回 false由基类设置 Failed
return true;
}
```
## 8. 结构体不可变性检测
> 依据:[类型设计准则](2.类型设计准则.md) — "❌ **禁止**定义可变值类型" / "✔️ 声明不可变值类型时使用 `readonly struct` 修饰符"
检查所有 `struct` 定义,**必须**声明为 `readonly struct`。存在可变 struct 即为违规。
检测要点:
- `struct` 声明缺少 `readonly` 修饰符
- 属性使用 `{ get; set; }` 而非 `{ get; }`
- 公共字段未声明为 `readonly`
- 缺少构造函数(依赖默认值逐字段赋值的模式)
```
❌ struct MutableConfig
{
public int Timeout { get; set; } // 可变属性
public string Name { get; set; }
}
✅ readonly struct ImmutableConfig
{
public int Timeout { get; } // 只读属性
public string Name { get; }
public ImmutableConfig(int timeout, string name)
{
Timeout = timeout;
Name = name;
}
}
```
**禁止可变 struct 的原因**
- 值类型按值复制,可变成员修改作用于副本而非原值,极易产生静默 bug
- `readonly` 字段持有可变 struct 时,编译器每次访问都会产生防御性复制,造成不必要的性能开销
- `readonly struct` 从根源上杜绝以上两类问题
## 9. 文件系统禁止重写 GetHashCode
检查所有 `IFileSystem` 实现类,确认未重写 `GetHashCode()``Equals()`
`BundleInfo.GetCombineKey()` 依赖 `RuntimeHelpers.GetHashCode(_fileSystem)` 获取基于引用标识的哈希值,用于下载器合并时的去重判断。如果文件系统实现类重写了 `GetHashCode()`,虽然 `RuntimeHelpers.GetHashCode` 本身不受影响,但会暗示该类型有自定义相等性语义,可能导致其他使用 `GetHashCode()` 的位置产生非预期的碰撞。
检测要点:
- `IFileSystem` 实现类中存在 `override int GetHashCode()` 声明
- `IFileSystem` 实现类中存在 `override bool Equals(object)` 声明
```
❌ class SandboxFileSystem : IFileSystem
{
public override int GetHashCode() => PackageName.GetHashCode(); // 破坏引用标识语义
}
✅ class SandboxFileSystem : IFileSystem
{
// 不重写 GetHashCode保持 object 默认的引用标识行为
}
```
## 10. 不可变结构体命名参数检测
检测所有 `readonly struct` 构造函数调用中**位置参数 > 3 个**的位置,确认是否使用命名参数提高可读性。
`readonly struct` 只能通过构造函数一次性传入所有字段,参数数量通常较多,且参数顺序是唯一区分手段。同类型参数相邻时,顺序错误不会产生编译错误,容易引入静默 bug。
重点检查以下场景:
- 构造函数参数 > 3 个
- 多个同类型参数相邻(如连续多个 `bool``int`、接口类型)
- 参数名与属性名高度对应,使用命名参数可消除歧义
```
❌ new Configuration(60, true, level, decryptor, backend, retryPolicy, urlPolicy);
✅ new Configuration(
watchdogTimeout: 60,
disableUnityWebCache: true,
downloadVerifyLevel: level,
assetBundleDecryptor: decryptor,
downloadBackend: backend,
downloadRetryPolicy: retryPolicy,
downloadUrlPolicy: urlPolicy);
```

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 200087470392cf24ebdd0efac8e44210
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.IO; using System.IO;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@@ -146,7 +146,7 @@ namespace YooAsset.Editor
string message = BuildLogger.GetErrorMessage(ErrorCode.BuildPipelineIsNullOrEmpty, "Build pipeline is null or empty !"); string message = BuildLogger.GetErrorMessage(ErrorCode.BuildPipelineIsNullOrEmpty, "Build pipeline is null or empty !");
throw new Exception(message); throw new Exception(message);
} }
if (BuildBundleType == (int)EBundleType.Unknown) if (BuildBundleType == (int)EBundleType.None)
{ {
string message = BuildLogger.GetErrorMessage(ErrorCode.BuildBundleTypeIsUnknown, $"Build bundle type is unknown {BuildBundleType} !"); string message = BuildLogger.GetErrorMessage(ErrorCode.BuildBundleTypeIsUnknown, $"Build bundle type is unknown {BuildBundleType} !");
throw new Exception(message); throw new Exception(message);

View File

@@ -54,8 +54,8 @@ namespace YooAsset.Editor
{ {
foreach (var packageBundle in manifest.BundleList) foreach (var packageBundle in manifest.BundleList)
{ {
string sourcePath = $"{packageOutputDirectory}/{packageBundle.FileName}"; string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
string destPath = $"{buildinRootDirectory}/{packageBundle.FileName}"; string destPath = $"{buildinRootDirectory}/{packageBundle.GetFileName()}";
EditorTools.CopyFile(sourcePath, destPath, true); EditorTools.CopyFile(sourcePath, destPath, true);
} }
} }
@@ -68,8 +68,8 @@ namespace YooAsset.Editor
{ {
if (packageBundle.HasAnyTag(tags) == false) if (packageBundle.HasAnyTag(tags) == false)
continue; continue;
string sourcePath = $"{packageOutputDirectory}/{packageBundle.FileName}"; string sourcePath = $"{packageOutputDirectory}/{packageBundle.GetFileName()}";
string destPath = $"{buildinRootDirectory}/{packageBundle.FileName}"; string destPath = $"{buildinRootDirectory}/{packageBundle.GetFileName()}";
EditorTools.CopyFile(sourcePath, destPath, true); EditorTools.CopyFile(sourcePath, destPath, true);
} }
} }

View File

@@ -35,7 +35,7 @@ namespace YooAsset.Editor
manifest.EnableAddressable = buildMapContext.Command.EnableAddressable; manifest.EnableAddressable = buildMapContext.Command.EnableAddressable;
manifest.SupportExtensionless = buildMapContext.Command.SupportExtensionless; manifest.SupportExtensionless = buildMapContext.Command.SupportExtensionless;
manifest.LocationToLower = buildMapContext.Command.LocationToLower; manifest.LocationToLower = buildMapContext.Command.LocationToLower;
manifest.IncludeAssetGUID = buildMapContext.Command.IncludeAssetGUID; manifest.IncludeAssetGuid = buildMapContext.Command.IncludeAssetGUID;
manifest.ReplaceAssetPathWithAddress = replaceAssetPathWithAddress; manifest.ReplaceAssetPathWithAddress = replaceAssetPathWithAddress;
manifest.OutputNameStyle = (int)buildParameters.FileNameStyle; manifest.OutputNameStyle = (int)buildParameters.FileNameStyle;
manifest.BuildBundleType = buildParameters.BuildBundleType; manifest.BuildBundleType = buildParameters.BuildBundleType;
@@ -152,7 +152,7 @@ namespace YooAsset.Editor
PackageAsset packageAsset = new PackageAsset(); PackageAsset packageAsset = new PackageAsset();
packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty; packageAsset.Address = buildMapContext.Command.EnableAddressable ? assetInfo.Address : string.Empty;
packageAsset.AssetPath = assetInfo.AssetInfo.AssetPath; packageAsset.AssetPath = assetInfo.AssetInfo.AssetPath;
packageAsset.AssetGUID = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty; packageAsset.AssetGuid = buildMapContext.Command.IncludeAssetGUID ? assetInfo.AssetInfo.AssetGUID : string.Empty;
packageAsset.AssetTags = assetInfo.AssetTags.ToArray(); packageAsset.AssetTags = assetInfo.AssetTags.ToArray();
packageAsset.EditorUserData = assetInfo; packageAsset.EditorUserData = assetInfo;
result.Add(packageAsset); result.Add(packageAsset);

View File

@@ -102,7 +102,7 @@ namespace YooAsset.Editor
{ {
ReportBundleInfo reportBundleInfo = new ReportBundleInfo(); ReportBundleInfo reportBundleInfo = new ReportBundleInfo();
reportBundleInfo.BundleName = packageBundle.BundleName; reportBundleInfo.BundleName = packageBundle.BundleName;
reportBundleInfo.FileName = packageBundle.FileName; reportBundleInfo.FileName = packageBundle.GetFileName();
reportBundleInfo.FileHash = packageBundle.FileHash; reportBundleInfo.FileHash = packageBundle.FileHash;
reportBundleInfo.FileCRC = packageBundle.FileCrc; reportBundleInfo.FileCRC = packageBundle.FileCrc;
reportBundleInfo.FileSize = packageBundle.FileSize; reportBundleInfo.FileSize = packageBundle.FileSize;

View File

@@ -1,4 +1,4 @@
using System; using System;
using System.Linq; using System.Linq;
using System.IO; using System.IO;
using System.Collections; using System.Collections;
@@ -24,15 +24,14 @@ namespace YooAsset.Editor
string pipelineOutputDirectory = buildParametersContext.GetPipelineOutputDirectory(); string pipelineOutputDirectory = buildParametersContext.GetPipelineOutputDirectory();
foreach (var bundleInfo in buildMapContext.Collection) foreach (var bundleInfo in buildMapContext.Collection)
{ {
BundleEncryptArgs args = new BundleEncryptArgs(); string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}";
args.BundleName = bundleInfo.BundleName; var args = new BundleEncryptArgs(bundleInfo.BundleName, filePath);
args.FilePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}";
var encryptResult = encryptionServices.Encrypt(args); var encryptResult = encryptionServices.Encrypt(args);
if (encryptResult.Encrypted) if (encryptResult.IsEncrypted)
{ {
string filePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt"; string encryptedFilePath = $"{pipelineOutputDirectory}/{bundleInfo.BundleName}.encrypt";
FileUtility.WriteAllBytes(filePath, encryptResult.EncryptedFileData); FileUtility.WriteAllBytes(filePath, encryptResult.EncryptedFileData);
bundleInfo.EncryptedFilePath = filePath; bundleInfo.EncryptedFilePath = encryptedFilePath;
bundleInfo.Encrypted = true; bundleInfo.Encrypted = true;
BuildLogger.Log($"Bundle file encryption complete: {filePath}"); BuildLogger.Log($"Bundle file encryption complete: {filePath}");
} }

View File

@@ -1,4 +1,4 @@
#if UNITY_2019_4_OR_NEWER #if UNITY_2019_4_OR_NEWER
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEditor; using UnityEditor;
@@ -255,7 +255,7 @@ namespace YooAsset.Editor
{ {
// 发送采集数据的命令 // 发送采集数据的命令
DiagnosticCommand command = new DiagnosticCommand(); DiagnosticCommand command = new DiagnosticCommand();
command.CommandType = (int)EDiagnosticCommandType.AutoSampling; command.CommandType = EDiagnosticCommandType.AutoSampling;
command.Parameter = evt.newValue ? "open" : "close"; command.Parameter = evt.newValue ? "open" : "close";
byte[] data = DiagnosticCommand.Serialize(command); byte[] data = DiagnosticCommand.Serialize(command);
EditorConnection.instance.Send(DiagnosticSystemConsts.EditorToPlayerMessageId, data); EditorConnection.instance.Send(DiagnosticSystemConsts.EditorToPlayerMessageId, data);
@@ -266,7 +266,7 @@ namespace YooAsset.Editor
{ {
// 发送采集数据的命令 // 发送采集数据的命令
DiagnosticCommand command = new DiagnosticCommand(); DiagnosticCommand command = new DiagnosticCommand();
command.CommandType = (int)EDiagnosticCommandType.SampleOnce; command.CommandType = EDiagnosticCommandType.SampleOnce;
command.Parameter = string.Empty; command.Parameter = string.Empty;
byte[] data = DiagnosticCommand.Serialize(command); byte[] data = DiagnosticCommand.Serialize(command);
EditorConnection.instance.Send(DiagnosticSystemConsts.EditorToPlayerMessageId, data); EditorConnection.instance.Send(DiagnosticSystemConsts.EditorToPlayerMessageId, data);

View File

@@ -379,7 +379,9 @@ namespace YooAsset.Editor
var sourceDatas = new List<ITableData>(providerInfo.Dependencies.Count); var sourceDatas = new List<ITableData>(providerInfo.Dependencies.Count);
foreach (var bundleName in providerInfo.Dependencies) foreach (var bundleName in providerInfo.Dependencies)
{ {
var dependBundleInfo = packageData.GetBundleInfo(bundleName); if (packageData.TryGetBundleInfo(bundleName, out var dependBundleInfo) == false)
continue;
var rowData = new DependTableData(); var rowData = new DependTableData();
rowData.BundleInfo = dependBundleInfo; rowData.BundleInfo = dependBundleInfo;
rowData.AddStringValueCell("DependBundles", dependBundleInfo.BundleName); rowData.AddStringValueCell("DependBundles", dependBundleInfo.BundleName);

View File

@@ -468,7 +468,9 @@ namespace YooAsset.Editor
var sourceDatas = new List<ITableData>(1000); var sourceDatas = new List<ITableData>(1000);
foreach (string referenceBundleName in selectBundleInfo.Referencers) foreach (string referenceBundleName in selectBundleInfo.Referencers)
{ {
var bundleInfo = packageData.GetBundleInfo(referenceBundleName); if (packageData.TryGetBundleInfo(referenceBundleName, out var bundleInfo) == false)
continue;
var rowData = new ReferenceTableData(); var rowData = new ReferenceTableData();
rowData.BundleInfo = bundleInfo; rowData.BundleInfo = bundleInfo;
rowData.AddStringValueCell("BundleName", bundleInfo.BundleName); rowData.AddStringValueCell("BundleName", bundleInfo.BundleName);

View File

@@ -33,10 +33,10 @@ namespace YooAsset
internal bool IsWaitForCompletion { get; private set; } internal bool IsWaitForCompletion { get; private set; }
/// <summary> /// <summary>
/// 判断当前帧时间切片是否已用完 /// 当前帧时间切片是否已用完
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 同步等待时始终返回 false以确保操作能持续执行直到完成 /// 同步等待时始终返回 false以确保操作能持续执行直到完成
/// </remarks> /// </remarks>
protected bool IsBusy protected bool IsBusy
{ {
@@ -73,7 +73,7 @@ namespace YooAsset
public float Progress { get; protected set; } public float Progress { get; protected set; }
/// <summary> /// <summary>
/// 判断异步操作是否已结束 /// 异步操作是否已结束
/// </summary> /// </summary>
public bool IsDone public bool IsDone
{ {
@@ -103,7 +103,7 @@ namespace YooAsset
/// 异步操作的完成事件 /// 异步操作的完成事件
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 若注册时操作已完成,回调将立即执行 /// 若注册时操作已完成,回调将立即执行
/// </remarks> /// </remarks>
public event Action<AsyncOperationBase> Completed public event Action<AsyncOperationBase> Completed
{ {
@@ -119,7 +119,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"Exception in completion callback: {ex}."); YooLogger.LogError($"Exception in completion callback: {ex}.");
} }
} }
else else
@@ -178,7 +178,7 @@ namespace YooAsset
{ {
_error = $"Operation '{GetType().Name}' did not complete during synchronous wait."; _error = $"Operation '{GetType().Name}' did not complete during synchronous wait.";
_status = EOperationStatus.Failed; _status = EOperationStatus.Failed;
YooLogger.Error(_error); YooLogger.LogError(_error);
} }
// 注意强制收尾确保Task能完成 // 注意强制收尾确保Task能完成
@@ -209,7 +209,7 @@ namespace YooAsset
// 内部逻辑抛出异常一律视为该异步任务失败。 // 内部逻辑抛出异常一律视为该异步任务失败。
_error = ex.ToString(); _error = ex.ToString();
_status = EOperationStatus.Failed; _status = EOperationStatus.Failed;
YooLogger.Error($"Exception in {GetType().Name}.InternalStart: {ex}."); YooLogger.LogError($"Exception in {GetType().Name}.InternalStart: {ex}.");
} }
// 注意:同步完成的操作立即收尾 // 注意:同步完成的操作立即收尾
@@ -243,7 +243,7 @@ namespace YooAsset
// 内部逻辑抛出异常一律视为该异步任务失败。 // 内部逻辑抛出异常一律视为该异步任务失败。
_error = ex.ToString(); _error = ex.ToString();
_status = EOperationStatus.Failed; _status = EOperationStatus.Failed;
YooLogger.Error($"Exception in {GetType().Name}.InternalUpdate: {ex}."); YooLogger.LogError($"Exception in {GetType().Name}.InternalUpdate: {ex}.");
} }
} }
@@ -271,7 +271,7 @@ namespace YooAsset
InternalAbort(); InternalAbort();
_error = "Operation was aborted."; _error = "Operation was aborted.";
_status = EOperationStatus.Failed; _status = EOperationStatus.Failed;
YooLogger.Warning($"Async operation '{GetType().Name}' has been aborted."); YooLogger.LogWarning($"Async operation '{GetType().Name}' has been aborted.");
} }
// 注意强制收尾确保Task能完成 // 注意强制收尾确保Task能完成
@@ -316,7 +316,7 @@ namespace YooAsset
/// 内部同步等待方法(子类可选实现) /// 内部同步等待方法(子类可选实现)
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 默认抛出异常,子类应重写以支持同步等待 /// 默认抛出异常,子类应重写以支持同步等待
/// </remarks> /// </remarks>
protected virtual void InternalWaitForCompletion() protected virtual void InternalWaitForCompletion()
{ {
@@ -327,7 +327,6 @@ namespace YooAsset
/// <summary> /// <summary>
/// 将操作标记为成功完成 /// 将操作标记为成功完成
/// </summary> /// </summary>
/// <exception cref="InvalidOperationException">操作已处于终结状态时抛出</exception>
protected void SetResult() protected void SetResult()
{ {
if (IsDone) if (IsDone)
@@ -340,7 +339,6 @@ namespace YooAsset
/// 将操作标记为失败 /// 将操作标记为失败
/// </summary> /// </summary>
/// <param name="error">错误描述</param> /// <param name="error">错误描述</param>
/// <exception cref="InvalidOperationException">操作已处于终结状态时抛出</exception>
protected void SetError(string error) protected void SetError(string error)
{ {
if (IsDone) if (IsDone)
@@ -350,6 +348,22 @@ namespace YooAsset
_status = EOperationStatus.Failed; _status = EOperationStatus.Failed;
} }
/// <summary>
/// 计算多阶段操作的整体进度
/// </summary>
/// <param name="stageIndex">当前阶段索引从0开始</param>
/// <param name="stageCount">阶段总数</param>
/// <param name="remaining">当前阶段剩余工作量</param>
/// <param name="total">当前阶段总工作量</param>
/// <returns>返回归一化的整体进度值0-1</returns>
protected float CalculateMultiStageProgress(int stageIndex, int stageCount, int remaining, int total)
{
if (total <= 0)
return (stageIndex + 1f) / stageCount;
float stageProgress = 1f - remaining / (float)total;
return (stageIndex + stageProgress) / stageCount;
}
/// <summary> /// <summary>
/// 添加子任务 /// 添加子任务
/// </summary> /// </summary>
@@ -391,7 +405,7 @@ namespace YooAsset
throw new YooInternalException("Child operation is null."); throw new YooInternalException("Child operation is null.");
if (_children.Contains(child) == false) if (_children.Contains(child) == false)
throw new YooInternalException($"Child operation '{child.GetType().Name}' was not found."); throw new YooInternalException($"Child operation '{child.GetType().Name}' not found.");
#endif #endif
_children.Remove(child); _children.Remove(child);
@@ -411,7 +425,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 批量执行一定次数的更新逻辑 /// 批量执行一定次数的更新逻辑
/// </summary> /// </summary>
/// <param name="count">最大执行次数默认1000次</param> /// <param name="count">最大执行次数默认1000次</param>
/// <remarks> /// <remarks>
/// 用于需要快速完成但又不想完全阻塞主线程的场景 /// 用于需要快速完成但又不想完全阻塞主线程的场景
/// </remarks> /// </remarks>
@@ -438,7 +452,7 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="sleepMilliseconds">每次循环后的休眠时长(毫秒)</param> /// <param name="sleepMilliseconds">每次循环后的休眠时长(毫秒)</param>
/// <remarks> /// <remarks>
/// 该方法会阻塞调用线程,每次更新之间会短暂休眠以避免占满 CPU /// 该方法会阻塞调用线程,每次更新之间会短暂休眠以避免占满 CPU
/// </remarks> /// </remarks>
protected void ExecuteUntilComplete(int sleepMilliseconds = 1) protected void ExecuteUntilComplete(int sleepMilliseconds = 1)
{ {
@@ -475,7 +489,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"Exception in {GetType().Name}.InternalDispose: {ex}."); YooLogger.LogError($"Exception in {GetType().Name}.InternalDispose: {ex}.");
} }
InvokeCompletedCallbacks(); InvokeCompletedCallbacks();
@@ -495,7 +509,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"Exception in completion callback: {ex}."); YooLogger.LogError($"Exception in completion callback: {ex}.");
} }
_completedCallback = null; _completedCallback = null;
} }
@@ -510,7 +524,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"Exception in completion callback: {ex}."); YooLogger.LogError($"Exception in completion callback: {ex}.");
} }
} }
_completedCallbackList = null; _completedCallbackList = null;

View File

@@ -47,7 +47,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 创建异步操作调度器实例 /// 创建异步操作调度器实例
/// </summary> /// </summary>
/// <param name="packageName">所属包裹的名称</param> /// <param name="packageName">所属包裹的名称</param>
/// <param name="creationOrder">创建顺序,用于同优先级时的稳定排序。</param> /// <param name="creationOrder">创建顺序,用于同优先级时的稳定排序。</param>
public AsyncOperationScheduler(string packageName, int creationOrder) public AsyncOperationScheduler(string packageName, int creationOrder)
{ {
@@ -60,7 +60,7 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="operation">要启动的异步操作</param> /// <param name="operation">要启动的异步操作</param>
/// <remarks> /// <remarks>
/// 操作立即启动,下一次 Update 时合并到执行队列 /// 操作立即启动,下一次 Update 时合并到执行队列
/// </remarks> /// </remarks>
public void StartOperation(AsyncOperationBase operation) public void StartOperation(AsyncOperationBase operation)
{ {
@@ -146,7 +146,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取调试信息 /// 获取调试信息
/// </summary> /// </summary>
/// <returns>包含所有运行中和待处理操作的诊断信息列表</returns> /// <returns>包含所有运行中和待处理操作的诊断信息列表</returns>
public List<DiagnosticOperationInfo> GetDiagnosticInfos() public List<DiagnosticOperationInfo> GetDiagnosticInfos()
{ {
int totalCount = _runningOperations.Count + _pendingOperations.Count; int totalCount = _runningOperations.Count + _pendingOperations.Count;

View File

@@ -6,7 +6,7 @@ using System.Diagnostics;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 异步操作系统,负责管理所有包裹的调度器 /// 异步操作系统,负责管理所有包裹的调度器
/// </summary> /// </summary>
internal static class AsyncOperationSystem internal static class AsyncOperationSystem
{ {
@@ -40,7 +40,7 @@ namespace YooAsset
/// </summary> /// </summary>
/// <value>最小值为 <see cref="MinTimeSlice"/> 毫秒,低于此值将被自动钳制。</value> /// <value>最小值为 <see cref="MinTimeSlice"/> 毫秒,低于此值将被自动钳制。</value>
/// <remarks> /// <remarks>
/// 设置过小会导致每帧可执行的操作极少,影响整体加载速度 /// 设置过小会导致每帧可执行的操作极少,影响整体加载速度
/// </remarks> /// </remarks>
public static long MaxTimeSlice public static long MaxTimeSlice
{ {
@@ -53,7 +53,7 @@ namespace YooAsset
if (value < MinTimeSlice) if (value < MinTimeSlice)
{ {
_maxTimeSlice = MinTimeSlice; _maxTimeSlice = MinTimeSlice;
YooLogger.Warning($"MaxTimeSlice must be at least {MinTimeSlice} ms, clamped to {MinTimeSlice}."); YooLogger.LogWarning($"MaxTimeSlice must be at least {MinTimeSlice} ms, clamped to {MinTimeSlice}.");
} }
else else
{ {
@@ -63,7 +63,7 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 判断当前帧的时间切片预算是否已用完 /// 当前帧的时间切片预算是否已用完
/// </summary> /// </summary>
public static bool IsBusy public static bool IsBusy
{ {
@@ -88,7 +88,7 @@ namespace YooAsset
{ {
if (_isInitialized) if (_isInitialized)
{ {
YooLogger.Warning("Operation system is already initialized."); YooLogger.LogWarning("Operation system is already initialized.");
return; return;
} }
@@ -232,7 +232,7 @@ namespace YooAsset
/// 设置指定包裹调度器的优先级 /// 设置指定包裹调度器的优先级
/// </summary> /// </summary>
/// <param name="packageName">包裹名称</param> /// <param name="packageName">包裹名称</param>
/// <param name="priority">优先级,值越大越优</param> /// <param name="priority">优先级,值越大越优</param>
public static void SetSchedulerPriority(string packageName, uint priority) public static void SetSchedulerPriority(string packageName, uint priority)
{ {
DebugCheckInitialized(packageName); DebugCheckInitialized(packageName);
@@ -245,7 +245,7 @@ namespace YooAsset
/// 获取指定包裹调度器的优先级 /// 获取指定包裹调度器的优先级
/// </summary> /// </summary>
/// <param name="packageName">包裹名称</param> /// <param name="packageName">包裹名称</param>
/// <returns>调度器的当前优先级</returns> /// <returns>调度器的当前优先级</returns>
public static uint GetSchedulerPriority(string packageName) public static uint GetSchedulerPriority(string packageName)
{ {
DebugCheckInitialized(packageName); DebugCheckInitialized(packageName);
@@ -272,8 +272,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取指定包裹中所有操作的诊断信息 /// 获取指定包裹中所有操作的诊断信息
/// </summary> /// </summary>
/// <param name="packageName">包裹名称</param> /// <param name="packageName">包裹名称</param>
/// <returns>该包裹下所有操作的诊断信息列表</returns> /// <returns>该包裹下所有操作的诊断信息列表</returns>
internal static List<DiagnosticOperationInfo> GetDiagnosticInfos(string packageName) internal static List<DiagnosticOperationInfo> GetDiagnosticInfos(string packageName)
{ {
DebugCheckInitialized(packageName); DebugCheckInitialized(packageName);

View File

@@ -27,7 +27,7 @@ namespace YooAsset
/// 获取操作结果 /// 获取操作结果
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 业务失败不视为异常,此处不抛出异常 /// 业务失败不视为异常,此处不抛出异常
/// </remarks> /// </remarks>
public void GetResult() public void GetResult()
{ {

View File

@@ -7,56 +7,51 @@ namespace YooAsset
/// </summary> /// </summary>
internal readonly struct EvictionResult internal readonly struct EvictionResult
{ {
private readonly bool _initialized;
/// <summary> /// <summary>
/// 错误信息 /// 错误信息
/// </summary> /// </summary>
public readonly string Error; public string Error { get; }
/// <summary> /// <summary>
/// 需要清理的资源标识符集合 /// 需要清理的资源标识符集合
/// </summary> /// </summary>
public readonly List<string> BundleGUIDs; public IReadOnlyList<string> BundleGuids { get; }
/// <summary> /// <summary>
/// 是否成功 /// 是否执行成功
/// </summary> /// </summary>
public bool Succeeded public bool Succeeded
{ {
get { return Error == null; } get { return _initialized && Error == null; }
} }
/// <summary> private EvictionResult(string error, IReadOnlyList<string> bundleGuids)
/// 创建失败结果
/// </summary>
public EvictionResult(string error)
{ {
_initialized = true;
Error = error; Error = error;
BundleGUIDs = null; BundleGuids = bundleGuids;
} }
/// <summary> /// <summary>
/// 创建成功结果 /// 创建表示执行成功的淘汰结果
/// </summary> /// </summary>
public EvictionResult(List<string> bundleGUIDs) /// <param name="bundleGuids">需要清理的资源包标识符列表</param>
/// <returns>携带待清理列表的成功结果</returns>
public static EvictionResult CreateSuccess(IReadOnlyList<string> bundleGuids)
{ {
Error = null; return new EvictionResult(null, bundleGuids);
BundleGUIDs = bundleGUIDs;
} }
/// <summary> /// <summary>
/// 创建成功结果 /// 创建表示执行失败的淘汰结果
/// </summary> /// </summary>
public static EvictionResult Success(List<string> bundleGUIDs) /// <param name="error">描述失败原因的错误信息</param>
/// <returns>携带错误信息的失败结果</returns>
public static EvictionResult CreateFailure(string error)
{ {
return new EvictionResult(bundleGUIDs); return new EvictionResult(error, null);
}
/// <summary>
/// 创建失败结果
/// </summary>
public static EvictionResult Failure(string error)
{
return new EvictionResult(error);
} }
} }
} }

View File

@@ -18,7 +18,7 @@ namespace YooAsset
string RootPath { get; } string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
bool IsReadOnly { get; } bool IsReadOnly { get; }
@@ -34,33 +34,44 @@ namespace YooAsset
/// <summary> /// <summary>
/// 初始化文件缓存系统 /// 初始化缓存系统
/// </summary> /// </summary>
/// <returns>初始化异步操作对象</returns>
BCInitializeOperation InitializeAsync(); BCInitializeOperation InitializeAsync();
/// <summary> /// <summary>
/// 写入缓存文件 /// 将资源包数据写入缓存
/// </summary> /// </summary>
/// <param name="options">写入操作的配置参数</param>
/// <returns>写入缓存异步操作对象</returns>
BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options); BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options);
/// <summary> /// <summary>
/// 清理缓存文件 /// 清理符合淘汰策略的缓存文件
/// </summary> /// </summary>
/// <param name="options">清理操作的配置参数</param>
/// <returns>清理缓存异步操作对象</returns>
BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options); BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options);
/// <summary> /// <summary>
/// 验证缓存文件 /// 验证缓存文件的完整性
/// </summary> /// </summary>
/// <param name="options">验证操作的配置参数</param>
/// <returns>验证缓存异步操作对象</returns>
BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options); BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options);
/// <summary> /// <summary>
/// 加载资源包 /// 加载指定资源包
/// </summary> /// </summary>
/// <param name="options">加载操作的配置参数</param>
/// <returns>加载资源包异步操作对象</returns>
BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options); BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options);
/// <summary> /// <summary>
/// 是否已缓存指定 Bundle /// 检查指定资源包是否已存在于缓存中
/// </summary> /// </summary>
bool IsCached(string bundleGUID); /// <param name="bundleGuid">资源包的唯一标识符</param>
/// <returns>如果缓存中存在该资源包则返回 true否则返回 false。</returns>
bool IsCached(string bundleGuid);
} }
} }

View File

@@ -9,6 +9,6 @@ namespace YooAsset
/// <summary> /// <summary>
/// Bundle 唯一标识 /// Bundle 唯一标识
/// </summary> /// </summary>
string BundleGUID { get; } string BundleGuid { get; }
} }
} }

View File

@@ -8,8 +8,11 @@ namespace YooAsset
internal interface ICacheEvictionPolicy internal interface ICacheEvictionPolicy
{ {
/// <summary> /// <summary>
/// 根据策略从缓存条目中选出需要清理的 BundleGUID 列表 /// 选出需要清理的 BundleGuid 列表
/// </summary> /// </summary>
/// <param name="cacheEntries">当前全部缓存条目的只读集合</param>
/// <param name="options">缓存清理的配置参数</param>
/// <returns>包含待清理 BundleGuid 列表的执行结果,失败时携带错误信息。</returns>
EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options); EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options);
} }
} }

View File

@@ -11,14 +11,22 @@ namespace YooAsset
/// <summary> /// <summary>
/// 清理缓存完成操作 /// 清理缓存完成操作
/// </summary> /// </summary>
internal class BCClearCacheCompleteOperation : BCClearCacheOperation internal sealed class BCClearCacheCompleteOperation : BCClearCacheOperation
{ {
private readonly string _error; private readonly string _error;
/// <summary>
/// 创建清理缓存完成操作实例
/// </summary>
public BCClearCacheCompleteOperation() public BCClearCacheCompleteOperation()
{ {
_error = null; _error = null;
} }
/// <summary>
/// 创建清理缓存完成操作实例
/// </summary>
/// <param name="error">错误信息</param>
public BCClearCacheCompleteOperation(string error) public BCClearCacheCompleteOperation(string error)
{ {
_error = error; _error = error;

View File

@@ -4,21 +4,28 @@ namespace YooAsset
/// <summary> /// <summary>
/// 清理缓存操作选项 /// 清理缓存操作选项
/// </summary> /// </summary>
internal struct BCClearCacheOptions internal readonly struct BCClearCacheOptions
{ {
/// <summary> /// <summary>
/// 清理方式 /// 清理方式
/// </summary> /// </summary>
public string ClearMethod { get; set; } public string ClearMethod { get; }
/// <summary> /// <summary>
/// 附加参数 /// 附加参数
/// </summary> /// </summary>
public object ClearParameter { get; set; } public object ClearParameter { get; }
/// <summary> /// <summary>
/// 资源清单 /// 资源清单
/// </summary> /// </summary>
public PackageManifest Manifest { get; set; } public PackageManifest Manifest { get; }
public BCClearCacheOptions(string clearMethod, object clearParameter, PackageManifest manifest)
{
ClearMethod = clearMethod;
ClearParameter = clearParameter;
Manifest = manifest;
}
} }
} }

View File

@@ -6,6 +6,9 @@ namespace YooAsset
/// </summary> /// </summary>
internal abstract class BCLoadBundleOperation : AsyncOperationBase internal abstract class BCLoadBundleOperation : AsyncOperationBase
{ {
/// <summary>
/// 资源包加载的内部结果
/// </summary>
protected readonly struct LoadResult protected readonly struct LoadResult
{ {
/// <summary> /// <summary>
@@ -14,22 +17,32 @@ namespace YooAsset
public readonly string Error; public readonly string Error;
/// <summary> /// <summary>
/// 是否成功 /// 加载是否成功
/// </summary> /// </summary>
public bool Succeeded public bool Succeeded
{ {
get { return Error == null; } get { return Error == null; }
} }
public LoadResult(string error) private LoadResult(string error)
{ {
Error = error; Error = error;
} }
/// <summary>
/// 创建表示加载成功的默认结果
/// </summary>
/// <returns>不携带错误信息的成功结果</returns>
public static LoadResult Default() public static LoadResult Default()
{ {
return new LoadResult(null); return new LoadResult(null);
} }
/// <summary>
/// 创建表示加载失败的结果
/// </summary>
/// <param name="error">错误信息</param>
/// <returns>携带错误信息的失败结果</returns>
public static LoadResult Failure(string error) public static LoadResult Failure(string error)
{ {
return new LoadResult(error); return new LoadResult(error);
@@ -49,6 +62,10 @@ namespace YooAsset
{ {
private readonly string _error; private readonly string _error;
/// <summary>
/// 创建加载资源包错误操作实例
/// </summary>
/// <param name="error">错误信息</param>
internal BCLoadBundleErrorOperation(string error) internal BCLoadBundleErrorOperation(string error)
{ {
_error = error; _error = error;

View File

@@ -7,10 +7,14 @@ namespace YooAsset
internal readonly struct BCLoadBundleOptions internal readonly struct BCLoadBundleOptions
{ {
/// <summary> /// <summary>
/// 资源包 /// 要加载的资源包
/// </summary> /// </summary>
public readonly PackageBundle Bundle; public PackageBundle Bundle { get; }
/// <summary>
/// 创建加载资源包操作选项实例
/// </summary>
/// <param name="bundle">要加载的资源包描述</param>
public BCLoadBundleOptions(PackageBundle bundle) public BCLoadBundleOptions(PackageBundle bundle)
{ {
Bundle = bundle; Bundle = bundle;

View File

@@ -11,14 +11,23 @@ namespace YooAsset
/// <summary> /// <summary>
/// 验证缓存完成操作 /// 验证缓存完成操作
/// </summary> /// </summary>
internal class BCVerifyCacheCompleteOperation : BCVerifyCacheOperation internal sealed class BCVerifyCacheCompleteOperation : BCVerifyCacheOperation
{ {
private readonly string _error; private readonly string _error;
/// <summary>
/// 创建验证缓存完成操作实例
/// </summary>
public BCVerifyCacheCompleteOperation() public BCVerifyCacheCompleteOperation()
{ {
_error = null; _error = null;
} }
/// <summary>
/// 创建验证缓存完成操作实例
/// </summary>
/// <param name="error">错误信息</param>
public BCVerifyCacheCompleteOperation(string error) public BCVerifyCacheCompleteOperation(string error)
{ {
_error = error; _error = error;

View File

@@ -4,16 +4,22 @@ namespace YooAsset
/// <summary> /// <summary>
/// 验证缓存操作选项 /// 验证缓存操作选项
/// </summary> /// </summary>
internal struct BCVerifyCacheOptions internal readonly struct BCVerifyCacheOptions
{ {
/// <summary> /// <summary>
/// 要验证的资源包 /// 要验证的资源包
/// </summary> /// </summary>
public PackageBundle Bundle { get; set; } public PackageBundle Bundle { get; }
/// <summary> /// <summary>
/// 失败后直接移除缓存条目 /// 失败后直接移除缓存条目
/// </summary> /// </summary>
public bool DeleteCacheEntryOnFailure { get; set; } public bool DeleteCacheEntryOnFailure { get; }
public BCVerifyCacheOptions(PackageBundle bundle, bool deleteCacheEntryOnFailure)
{
Bundle = bundle;
DeleteCacheEntryOnFailure = deleteCacheEntryOnFailure;
}
} }
} }

View File

@@ -11,14 +11,22 @@ namespace YooAsset
/// <summary> /// <summary>
/// 写入缓存完成操作 /// 写入缓存完成操作
/// </summary> /// </summary>
internal class BCWriteCacheCompleteOperation : BCWriteCacheOperation internal sealed class BCWriteCacheCompleteOperation : BCWriteCacheOperation
{ {
private readonly string _error; private readonly string _error;
/// <summary>
/// 创建写入缓存完成操作实例
/// </summary>
public BCWriteCacheCompleteOperation() public BCWriteCacheCompleteOperation()
{ {
_error = null; _error = null;
} }
/// <summary>
/// 创建写入缓存完成操作实例
/// </summary>
/// <param name="error">错误信息</param>
public BCWriteCacheCompleteOperation(string error) public BCWriteCacheCompleteOperation(string error)
{ {
_error = error; _error = error;

View File

@@ -4,21 +4,28 @@ namespace YooAsset
/// <summary> /// <summary>
/// 写入缓存操作选项 /// 写入缓存操作选项
/// </summary> /// </summary>
internal struct BCWriteCacheOptions internal readonly struct BCWriteCacheOptions
{ {
/// <summary> /// <summary>
/// 要缓存的资源包 /// 要缓存的资源包
/// </summary> /// </summary>
public PackageBundle Bundle { get; set; } public PackageBundle Bundle { get; }
/// <summary> /// <summary>
/// 要缓存的文件路径 /// 要缓存的文件路径
/// </summary> /// </summary>
public string FilePath { get; set; } public string FilePath { get; }
/// <summary> /// <summary>
/// 要缓存的文件数据(可选) /// 要缓存的文件数据(可选)
/// </summary> /// </summary>
public byte[] FileData { get; set; } public byte[] FileData { get; }
public BCWriteCacheOptions(PackageBundle bundle, string filePath, byte[] fileData = null)
{
Bundle = bundle;
FilePath = filePath;
FileData = fileData;
}
} }
} }

View File

@@ -28,6 +28,10 @@ namespace YooAsset
/// </summary> /// </summary>
public BuiltinCatalog Catalog; public BuiltinCatalog Catalog;
/// <summary>
/// 创建加载内置资源目录操作实例
/// </summary>
/// <param name="options">加载内置资源目录的配置选项</param>
internal LoadBuiltinCatalogOperation(LoadBuiltinCatalogOptions options) internal LoadBuiltinCatalogOperation(LoadBuiltinCatalogOptions options)
{ {
_options = options; _options = options;
@@ -69,7 +73,10 @@ namespace YooAsset
if (_downloadBytesRequest == null) if (_downloadBytesRequest == null)
{ {
string url = DownloadUrlHelper.ToLocalFileUrl(_options.FilePath); string url = DownloadUrlHelper.ToLocalFileUrl(_options.FilePath);
var args = new DownloadDataRequestArgs(url, 60, 0); var args = new DownloadDataRequestArgs(
url: url,
timeout: 60,
watchdogTimeout: 0);
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args); _downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
_downloadBytesRequest.SendRequest(); _downloadBytesRequest.SendRequest();
} }

View File

@@ -4,21 +4,28 @@ namespace YooAsset
/// <summary> /// <summary>
/// 加载内置资源目录操作选项 /// 加载内置资源目录操作选项
/// </summary> /// </summary>
internal struct LoadBuiltinCatalogOptions internal readonly struct LoadBuiltinCatalogOptions
{ {
/// <summary> /// <summary>
/// 包裹名称 /// 包裹名称
/// </summary> /// </summary>
public string PackageName { get; set; } public string PackageName { get; }
/// <summary> /// <summary>
/// 文件路径 /// 文件路径
/// </summary> /// </summary>
public string FilePath { get; set; } public string FilePath { get; }
/// <summary> /// <summary>
/// 下载后台 /// 下载后台
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; set; } public IDownloadBackend DownloadBackend { get; }
public LoadBuiltinCatalogOptions(string packageName, string filePath, IDownloadBackend downloadBackend)
{
PackageName = packageName;
FilePath = filePath;
DownloadBackend = downloadBackend;
}
} }
} }

View File

@@ -6,7 +6,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从本地文件加载 AssetBundle 操作 /// 从本地文件加载 AssetBundle 操作
/// </summary> /// </summary>
internal class LoadLocalAssetBundleOperation : BCLoadBundleOperation internal sealed class LoadLocalAssetBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -27,6 +27,10 @@ namespace YooAsset
/// </summary> /// </summary>
public bool UnityEngineLoadFailed { get; private set; } = false; public bool UnityEngineLoadFailed { get; private set; } = false;
/// <summary>
/// 创建本地 AssetBundle 加载操作实例
/// </summary>
/// <param name="options">从本地加载 AssetBundle 的配置选项</param>
public LoadLocalAssetBundleOperation(LoadLocalAssetBundleOptions options) public LoadLocalAssetBundleOperation(LoadLocalAssetBundleOptions options)
{ {
_options = options; _options = options;
@@ -95,7 +99,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 强制挂起主线程(注意:该操作会很耗时) // 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Suspending the main thread to load Unity bundle."); YooLogger.LogWarning("Suspending the main thread to load Unity bundle.");
_assetBundle = _createRequest.assetBundle; _assetBundle = _createRequest.assetBundle;
} }
else else
@@ -135,10 +139,11 @@ namespace YooAsset
} }
private LoadResult LoadFromFileWithOffset(IBundleOffsetDecryptor decryptor) private LoadResult LoadFromFileWithOffset(IBundleOffsetDecryptor decryptor)
{ {
var args = new BundleDecryptArgs(); var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
args.Bundle = _options.Bundle; long rawOffset = decryptor.GetFileOffset(args);
args.FilePath = _options.FilePath; if (rawOffset < 0)
ulong offset = (ulong)decryptor.GetFileOffset(args); return LoadResult.Failure($"{_options.CacheName} decryptor returned negative offset: {rawOffset}.");
ulong offset = (ulong)rawOffset;
if (IsWaitForCompletion) if (IsWaitForCompletion)
_assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, offset); _assetBundle = AssetBundle.LoadFromFile(_options.FilePath, 0, offset);
@@ -149,9 +154,7 @@ namespace YooAsset
} }
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor) private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
{ {
var args = new BundleDecryptArgs(); var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
args.Bundle = _options.Bundle;
args.FilePath = _options.FilePath;
var binaryData = decryptor.GetDecryptedData(args); var binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null) if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data."); return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");
@@ -165,9 +168,7 @@ namespace YooAsset
} }
private LoadResult LoadFromStream(IBundleStreamDecryptor decryptor) private LoadResult LoadFromStream(IBundleStreamDecryptor decryptor)
{ {
var args = new BundleDecryptArgs(); var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
args.Bundle = _options.Bundle;
args.FilePath = _options.FilePath;
uint bufferSize = (uint)decryptor.GetBufferSize(args); uint bufferSize = (uint)decryptor.GetBufferSize(args);
_loadStream = decryptor.CreateDecryptionStream(args); _loadStream = decryptor.CreateDecryptionStream(args);
if (_loadStream == null) if (_loadStream == null)

View File

@@ -4,27 +4,35 @@ namespace YooAsset
/// <summary> /// <summary>
/// 加载 AssetBundle 的上下文信息 /// 加载 AssetBundle 的上下文信息
/// </summary> /// </summary>
internal struct LoadLocalAssetBundleOptions internal readonly struct LoadLocalAssetBundleOptions
{ {
/// <summary> /// <summary>
/// 文件缓存名称 /// 文件缓存名称
/// </summary> /// </summary>
public string CacheName { get; set; } public string CacheName { get; }
/// <summary> /// <summary>
/// 资源包信息 /// 资源包描述
/// </summary> /// </summary>
public PackageBundle Bundle { get; set; } public PackageBundle Bundle { get; }
/// <summary> /// <summary>
/// 文件加载路径 /// 文件加载路径
/// </summary> /// </summary>
public string FilePath { get; set; } public string FilePath { get; }
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
public LoadLocalAssetBundleOptions(string cacheName, PackageBundle bundle, string filePath, IBundleDecryptor assetBundleDecryptor)
{
CacheName = cacheName;
Bundle = bundle;
FilePath = filePath;
AssetBundleDecryptor = assetBundleDecryptor;
}
} }
} }

View File

@@ -6,7 +6,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从本地加载 RawBundle 操作 /// 从本地加载 RawBundle 操作
/// </summary> /// </summary>
internal class LoadLocalRawBundleOperation : BCLoadBundleOperation internal sealed class LoadLocalRawBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -16,10 +16,14 @@ namespace YooAsset
Done, Done,
} }
protected readonly LoadLocalRawBundleOptions _options; private readonly LoadLocalRawBundleOptions _options;
private RawBundle _rawBundle; private RawBundle _rawBundle;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建本地 RawBundle 加载操作实例
/// </summary>
/// <param name="options">从本地加载 RawBundle 的配置选项</param>
public LoadLocalRawBundleOperation(LoadLocalRawBundleOptions options) public LoadLocalRawBundleOperation(LoadLocalRawBundleOptions options)
{ {
_options = options; _options = options;
@@ -44,7 +48,13 @@ namespace YooAsset
return; return;
} }
LoadFromFile(); LoadResult result = LoadFromFile();
if (result.Succeeded == false)
{
_steps = ESteps.Done;
SetError(result.Error);
return;
}
} }
else else
{ {
@@ -99,16 +109,22 @@ namespace YooAsset
ExecuteBatch(); ExecuteBatch();
} }
private void LoadFromFile() private LoadResult LoadFromFile()
{
try
{ {
byte[] data = File.ReadAllBytes(_options.FilePath); byte[] data = File.ReadAllBytes(_options.FilePath);
_rawBundle = new RawBundle(data); _rawBundle = new RawBundle(data);
return LoadResult.Default();
}
catch (Exception ex)
{
return LoadResult.Failure($"Failed to read raw bundle file: {ex.Message}.");
}
} }
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor) private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor)
{ {
var args = new BundleDecryptArgs(); var args = new BundleDecryptArgs(_options.Bundle, null, _options.FilePath);
args.Bundle = _options.Bundle;
args.FilePath = _options.FilePath;
var binaryData = decryptor.GetDecryptedData(args); var binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null) if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data."); return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");

View File

@@ -4,26 +4,34 @@ namespace YooAsset
/// <summary> /// <summary>
/// 加载 RawBundle 的上下文信息 /// 加载 RawBundle 的上下文信息
/// </summary> /// </summary>
internal struct LoadLocalRawBundleOptions internal readonly struct LoadLocalRawBundleOptions
{ {
/// <summary> /// <summary>
/// 文件缓存名称 /// 文件缓存名称
/// </summary> /// </summary>
public string CacheName { get; set; } public string CacheName { get; }
/// <summary> /// <summary>
/// 资源包信息 /// 资源包描述
/// </summary> /// </summary>
public PackageBundle Bundle { get; set; } public PackageBundle Bundle { get; }
/// <summary> /// <summary>
/// 文件加载路径 /// 文件加载路径
/// </summary> /// </summary>
public string FilePath { get; set; } public string FilePath { get; }
/// <summary> /// <summary>
/// RawBundle 解密器 /// RawBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor RawBundleDecryptor { get; set; } public IBundleDecryptor RawBundleDecryptor { get; }
public LoadLocalRawBundleOptions(string cacheName, PackageBundle bundle, string filePath, IBundleDecryptor rawBundleDecryptor)
{
CacheName = cacheName;
Bundle = bundle;
FilePath = filePath;
RawBundleDecryptor = rawBundleDecryptor;
}
} }
} }

View File

@@ -12,7 +12,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从网络加载未加密 AssetBundle 操作 /// 从网络加载未加密 AssetBundle 操作
/// </summary> /// </summary>
internal class LoadWebNormalAssetBundleOperation : LoadWebAssetBundleOperation internal sealed class LoadWebNormalAssetBundleOperation : LoadWebAssetBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -28,6 +28,10 @@ namespace YooAsset
private IDownloadAssetBundleRequest _downloadAssetBundleRequest; private IDownloadAssetBundleRequest _downloadAssetBundleRequest;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建网络未加密 AssetBundle 加载操作实例
/// </summary>
/// <param name="options">从网络加载 AssetBundle 的配置选项</param>
public LoadWebNormalAssetBundleOperation(LoadWebAssetBundleOptions options) public LoadWebNormalAssetBundleOperation(LoadWebAssetBundleOptions options)
{ {
_options = options; _options = options;
@@ -47,7 +51,13 @@ namespace YooAsset
if (_steps == ESteps.BundleRequest) if (_steps == ESteps.BundleRequest)
{ {
string url = GetRequestUrl(); string url = GetRequestUrl();
var args = new DownloadAssetBundleRequestArgs(url, 0, _options.WatchdogTimeout, _options.DisableUnityWebCache, _options.Bundle.FileHash, _options.Bundle.UnityCrc); 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 = _options.DownloadBackend.CreateAssetBundleRequest(args);
_downloadAssetBundleRequest.SendRequest(); _downloadAssetBundleRequest.SendRequest();
_steps = ESteps.CheckRequest; _steps = ESteps.CheckRequest;
@@ -66,7 +76,7 @@ namespace YooAsset
if (assetBundle == null) if (assetBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Fatal error: downloaded asset bundle is null."); SetError("Downloaded asset bundle is null.");
} }
else else
{ {
@@ -128,7 +138,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从网络加载加密的 AssetBundle 操作 /// 从网络加载加密的 AssetBundle 操作
/// </summary> /// </summary>
internal class LoadWebEncryptedAssetBundleOperation : LoadWebAssetBundleOperation internal sealed class LoadWebEncryptedAssetBundleOperation : LoadWebAssetBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -149,6 +159,10 @@ namespace YooAsset
private AssetBundleCreateRequest _createRequest; private AssetBundleCreateRequest _createRequest;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建网络加密 AssetBundle 加载操作实例
/// </summary>
/// <param name="options">从网络加载 AssetBundle 的配置选项</param>
public LoadWebEncryptedAssetBundleOperation(LoadWebAssetBundleOptions options) public LoadWebEncryptedAssetBundleOperation(LoadWebAssetBundleOptions options)
{ {
_options = options; _options = options;
@@ -179,7 +193,10 @@ namespace YooAsset
{ {
_decryptor = decryptor as IBundleMemoryDecryptor; _decryptor = decryptor as IBundleMemoryDecryptor;
string url = GetRequestUrl(); string url = GetRequestUrl();
var args = new DownloadDataRequestArgs(url, 0, _options.WatchdogTimeout); var args = new DownloadDataRequestArgs(
url: url,
timeout: 0,
watchdogTimeout: _options.WatchdogTimeout);
_downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args); _downloadBytesRequest = _options.DownloadBackend.CreateBytesRequest(args);
_downloadBytesRequest.SendRequest(); _downloadBytesRequest.SendRequest();
_steps = ESteps.CheckRequest; _steps = ESteps.CheckRequest;
@@ -231,7 +248,7 @@ namespace YooAsset
else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High) else if (_options.DownloadVerifyLevel == EFileVerifyLevel.High)
verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc); verifyResult = FileVerifyHelper.VerifyFile(_downloadBytesRequest.Result, _options.Bundle.FileSize, _options.Bundle.FileCrc);
else else
throw new System.NotImplementedException(_options.DownloadVerifyLevel.ToString()); throw new YooInternalException($"Unexpected verify level: {_options.DownloadVerifyLevel}.");
if (verifyResult == EFileVerifyResult.Succeed) if (verifyResult == EFileVerifyResult.Succeed)
{ {
@@ -240,7 +257,7 @@ namespace YooAsset
else else
{ {
string error = $"[WebBundleVerify] Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}."; string error = $"[WebBundleVerify] Verify failed. Url: '{_downloadBytesRequest.Url}' Level: {_options.DownloadVerifyLevel} Result: {verifyResult}.";
YooLogger.Warning(error); YooLogger.LogWarning(error);
if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining()) if (IsWaitForCompletion == false && _downloadRetryController.HasRetriesRemaining())
{ {
@@ -314,9 +331,7 @@ namespace YooAsset
private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData) private LoadResult LoadFromMemory(IBundleMemoryDecryptor decryptor, byte[] fileData)
{ {
var args = new BundleDecryptArgs(); var args = new BundleDecryptArgs(_options.Bundle, fileData, null);
args.Bundle = _options.Bundle;
args.FileData = fileData;
var binaryData = decryptor.GetDecryptedData(args); var binaryData = decryptor.GetDecryptedData(args);
if (binaryData == null) if (binaryData == null)
return LoadResult.Failure($"{_options.CacheName} decryptor returned null data."); return LoadResult.Failure($"{_options.CacheName} decryptor returned null data.");

View File

@@ -5,56 +5,70 @@ namespace YooAsset
/// <summary> /// <summary>
/// 加载 AssetBundle 的上下文信息 /// 加载 AssetBundle 的上下文信息
/// </summary> /// </summary>
internal struct LoadWebAssetBundleOptions internal readonly struct LoadWebAssetBundleOptions
{ {
/// <summary> /// <summary>
/// 文件缓存名称 /// 文件缓存名称
/// </summary> /// </summary>
public string CacheName { get; set; } public string CacheName { get; }
/// <summary> /// <summary>
/// 资源包信息 /// 资源包描述
/// </summary> /// </summary>
public PackageBundle Bundle { get; set; } public PackageBundle Bundle { get; }
/// <summary> /// <summary>
/// 候选下载地址列表 /// 候选下载地址列表
/// </summary> /// </summary>
public IReadOnlyList<string> CandidateUrls { get; set; } public IReadOnlyList<string> CandidateUrls { get; }
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary> /// <summary>
/// 下载后台接口 /// 下载后台接口
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; set; } public IDownloadBackend DownloadBackend { get; }
/// <summary> /// <summary>
/// 下载数据校验级别 /// 下载数据校验级别
/// </summary> /// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; set; } public EFileVerifyLevel DownloadVerifyLevel { get; }
/// <summary> /// <summary>
/// 看门狗超时时间 /// 看门狗超时时间
/// </summary> /// </summary>
public int WatchdogTimeout { get; set; } public int WatchdogTimeout { get; }
/// <summary> /// <summary>
/// 禁用Unity的网络缓存 /// 禁用Unity的网络缓存
/// </summary> /// </summary>
public bool DisableUnityWebCache { get; set; } public bool DisableUnityWebCache { get; }
/// <summary> /// <summary>
/// 下载重试判定策略 /// 下载重试判定策略
/// </summary> /// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; set; } public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary> /// <summary>
/// URL 选择策略 /// URL 选择策略
/// </summary> /// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; set; } public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public LoadWebAssetBundleOptions(string cacheName, PackageBundle bundle, IReadOnlyList<string> candidateUrls, IBundleDecryptor assetBundleDecryptor, IDownloadBackend downloadBackend, EFileVerifyLevel downloadVerifyLevel, int watchdogTimeout, bool disableUnityWebCache, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
CacheName = cacheName;
Bundle = bundle;
CandidateUrls = candidateUrls;
AssetBundleDecryptor = assetBundleDecryptor;
DownloadBackend = downloadBackend;
DownloadVerifyLevel = downloadVerifyLevel;
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
} }
} }

View File

@@ -10,12 +10,12 @@ namespace YooAsset
/// <inheritdoc /> /// <inheritdoc />
public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options) public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options)
{ {
var bundleGUIDs = new List<string>(cacheEntries.Count); var bundleGuids = new List<string>(cacheEntries.Count);
foreach (var entry in cacheEntries) foreach (var entry in cacheEntries)
{ {
bundleGUIDs.Add(entry.BundleGUID); bundleGuids.Add(entry.BundleGuid);
} }
return EvictionResult.Success(bundleGUIDs); return EvictionResult.CreateSuccess(bundleGuids);
} }
} }
} }

View File

@@ -14,9 +14,9 @@ namespace YooAsset
public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options) public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options)
{ {
if (options.Manifest == null) if (options.Manifest == null)
return EvictionResult.Failure("Active package manifest not found."); return EvictionResult.CreateFailure("Active package manifest not found.");
if (options.ClearParameter == null) if (options.ClearParameter == null)
return EvictionResult.Failure("Clear param is null."); return EvictionResult.CreateFailure("Clear param is null.");
string[] locations; string[] locations;
if (options.ClearParameter is string str) if (options.ClearParameter is string str)
@@ -26,19 +26,19 @@ namespace YooAsset
else if (options.ClearParameter is string[] array) else if (options.ClearParameter is string[] array)
locations = array; locations = array;
else else
return EvictionResult.Failure($"Invalid clear param: {options.ClearParameter.GetType().FullName}"); return EvictionResult.CreateFailure($"Invalid clear param: {options.ClearParameter.GetType().FullName}");
var bundleGUIDs = new List<string>(locations.Length); var bundleGuids = new List<string>(locations.Length);
foreach (var location in locations) foreach (var location in locations)
{ {
string assetPath = options.Manifest.TryMappingToAssetPath(location); string assetPath = options.Manifest.TryMappingToAssetPath(location);
if (options.Manifest.TryGetPackageAsset(assetPath, out PackageAsset packageAsset)) if (options.Manifest.TryGetPackageAsset(assetPath, out PackageAsset packageAsset))
{ {
PackageBundle bundle = options.Manifest.GetMainPackageBundle(packageAsset.BundleID); PackageBundle bundle = options.Manifest.GetMainPackageBundle(packageAsset.BundleID);
bundleGUIDs.Add(bundle.BundleGuid); bundleGuids.Add(bundle.BundleGuid);
} }
} }
return EvictionResult.Success(bundleGUIDs); return EvictionResult.CreateSuccess(bundleGuids);
} }
} }
} }

View File

@@ -14,9 +14,9 @@ namespace YooAsset
public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options) public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options)
{ {
if (options.Manifest == null) if (options.Manifest == null)
return EvictionResult.Failure("Active package manifest not found."); return EvictionResult.CreateFailure("Active package manifest not found.");
if (options.ClearParameter == null) if (options.ClearParameter == null)
return EvictionResult.Failure("Clear param is null."); return EvictionResult.CreateFailure("Clear param is null.");
string[] tags; string[] tags;
if (options.ClearParameter is string str) if (options.ClearParameter is string str)
@@ -26,18 +26,18 @@ namespace YooAsset
else if (options.ClearParameter is string[] array) else if (options.ClearParameter is string[] array)
tags = array; tags = array;
else else
return EvictionResult.Failure($"Invalid clear param: {options.ClearParameter.GetType().FullName}"); return EvictionResult.CreateFailure($"Invalid clear param: {options.ClearParameter.GetType().FullName}");
var bundleGUIDs = new List<string>(cacheEntries.Count); var bundleGuids = new List<string>(cacheEntries.Count);
foreach (var entry in cacheEntries) foreach (var entry in cacheEntries)
{ {
if (options.Manifest.TryGetPackageBundleByBundleGUID(entry.BundleGUID, out PackageBundle bundle)) if (options.Manifest.TryGetPackageBundleByBundleGuid(entry.BundleGuid, out PackageBundle bundle))
{ {
if (bundle.HasAnyTag(tags)) if (bundle.HasAnyTag(tags))
bundleGUIDs.Add(bundle.BundleGuid); bundleGuids.Add(bundle.BundleGuid);
} }
} }
return EvictionResult.Success(bundleGUIDs); return EvictionResult.CreateSuccess(bundleGuids);
} }
} }
} }

View File

@@ -11,17 +11,17 @@ namespace YooAsset
public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options) public EvictionResult SelectEvictionTargets(IReadOnlyCollection<ICacheEntry> cacheEntries, BCClearCacheOptions options)
{ {
if (options.Manifest == null) if (options.Manifest == null)
return EvictionResult.Failure("Active package manifest not found."); return EvictionResult.CreateFailure("Active package manifest not found.");
var bundleGUIDs = new List<string>(cacheEntries.Count); var bundleGuids = new List<string>(cacheEntries.Count);
foreach (var entry in cacheEntries) foreach (var entry in cacheEntries)
{ {
if (options.Manifest.ContainsBundle(entry.BundleGUID) == false) if (options.Manifest.ContainsBundle(entry.BundleGuid) == false)
{ {
bundleGUIDs.Add(entry.BundleGUID); bundleGuids.Add(entry.BundleGuid);
} }
} }
return EvictionResult.Success(bundleGUIDs); return EvictionResult.CreateSuccess(bundleGuids);
} }
} }
} }

View File

@@ -4,29 +4,36 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 内置文件缓存系统,用于管理 StreamingAssets 中的资源包 /// 内置文件缓存系统,用于管理 StreamingAssets 中的资源包
/// </summary> /// </summary>
internal class BuiltinBundleCache : IBundleCache internal class BuiltinBundleCache : IBundleCache
{ {
/// <summary> /// <summary>
/// 内置文件缓存配置 /// 内置文件缓存配置
/// </summary> /// </summary>
internal struct Configuration internal readonly struct Configuration
{ {
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary> /// <summary>
/// RawBundle 解密器 /// RawBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor RawBundleDecryptor { get; set; } public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary> /// <summary>
/// 下载后台 /// 下载后台
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; set; } public IDownloadBackend DownloadBackend { get; }
public Configuration(IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IDownloadBackend downloadBackend)
{
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
DownloadBackend = downloadBackend;
}
} }
private readonly Dictionary<string, BuiltinBundleCacheEntry> _cacheEntries = new Dictionary<string, BuiltinBundleCacheEntry>(10000); private readonly Dictionary<string, BuiltinBundleCacheEntry> _cacheEntries = new Dictionary<string, BuiltinBundleCacheEntry>(10000);
@@ -48,7 +55,7 @@ namespace YooAsset
public string RootPath { get; } public string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
public bool IsReadOnly { get; } public bool IsReadOnly { get; }
@@ -88,62 +95,64 @@ namespace YooAsset
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCInitializeOperation InitializeAsync() public BCInitializeOperation InitializeAsync()
{ {
var operation = new BBCInitializeOperation(this); var operation = new BBCInitializeOperation(this);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options) public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{ {
var operation = new BCWriteCacheCompleteOperation($"{nameof(BuiltinBundleCache)} is readonly."); var operation = new BCWriteCacheCompleteOperation($"{nameof(BuiltinBundleCache)} is readonly.");
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options) public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{ {
var operation = new BCClearCacheCompleteOperation(); var operation = new BCClearCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options) public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{ {
var operation = new BCVerifyCacheCompleteOperation(); var operation = new BCVerifyCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options) public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{ {
if (options.Bundle.BundleType == (int)EBundleType.AssetBundle) if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
{ {
var operation = new BBCLoadAssetBundleOperation(this, options.Bundle); var operation = new BBCLoadAssetBundleOperation(this, options.Bundle);
return operation; return operation;
} }
else if (options.Bundle.BundleType == (int)EBundleType.RawBundle) else if (options.Bundle.GetBundleType() == (int)EBundleType.RawBundle)
{ {
var operation = new BBCLoadRawBundleOperation(this, options.Bundle); var operation = new BBCLoadRawBundleOperation(this, options.Bundle);
return operation; return operation;
} }
else else
{ {
string error = $"{nameof(BuiltinBundleCache)} does not support bundle type: {options.Bundle.BundleType}."; string error = $"{nameof(BuiltinBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error); var operation = new BCLoadBundleErrorOperation(error);
return operation; return operation;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsCached(string bundleGUID) public bool IsCached(string bundleGuid)
{ {
return _cacheEntries.ContainsKey(bundleGUID); return _cacheEntries.ContainsKey(bundleGuid);
} }
#region #region
/// <summary> /// <summary>
/// 获取指定缓存条目 /// 获取指定缓存条目
/// </summary> /// </summary>
internal BuiltinBundleCacheEntry GetEntry(string bundleGUID) /// <param name="bundleGuid">资源包 GUID</param>
/// <returns>对应的内置缓存条目</returns>
internal BuiltinBundleCacheEntry GetEntry(string bundleGuid)
{ {
if (_cacheEntries.TryGetValue(bundleGUID, out BuiltinBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundleGuid, out BuiltinBundleCacheEntry entry))
return entry; return entry;
else else
return null; return null;
@@ -152,17 +161,20 @@ namespace YooAsset
/// <summary> /// <summary>
/// 添加指定缓存条目 /// 添加指定缓存条目
/// </summary> /// </summary>
internal void AddEntry(string bundleGUID, BuiltinBundleCacheEntry cacheEntry) /// <param name="bundleGuid">资源包 GUID</param>
/// <param name="cacheEntry">内置缓存条目</param>
internal void AddEntry(string bundleGuid, BuiltinBundleCacheEntry cacheEntry)
{ {
if (_cacheEntries.ContainsKey(bundleGUID)) if (_cacheEntries.ContainsKey(bundleGuid))
throw new YooInternalException($"Cache entry already exists: '{bundleGUID}'."); throw new YooInternalException($"Cache entry already exists: '{bundleGuid}'.");
_cacheEntries.Add(bundleGUID, cacheEntry); _cacheEntries.Add(bundleGuid, cacheEntry);
} }
/// <summary> /// <summary>
/// 获取Catalog文件加载路径 /// 获取Catalog文件加载路径
/// </summary> /// </summary>
/// <returns>完整加载路径</returns>
internal string GetCatalogBinaryFileLoadPath() internal string GetCatalogBinaryFileLoadPath()
{ {
return PathUtility.Combine(RootPath, BuiltinCatalogConsts.BinaryFileName); return PathUtility.Combine(RootPath, BuiltinCatalogConsts.BinaryFileName);

View File

@@ -9,7 +9,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 资源包文件路径 /// 资源包文件路径
@@ -17,13 +17,13 @@ namespace YooAsset
public string FilePath { get; private set; } public string FilePath { get; private set; }
/// <summary> /// <summary>
/// 创建内置文件缓存条目 /// 创建内置文件缓存条目实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="filePath">资源包文件路径</param> /// <param name="filePath">资源包文件路径</param>
public BuiltinBundleCacheEntry(string bundleGUID, string filePath) public BuiltinBundleCacheEntry(string bundleGuid, string filePath)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
FilePath = filePath; FilePath = filePath;
} }
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
namespace YooAsset namespace YooAsset
@@ -19,7 +18,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID; public string BundleGuid;
/// <summary> /// <summary>
/// 资源包文件名 /// 资源包文件名

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 内置资源目录常量定义 /// 内置资源目录常量定义
/// </summary> /// </summary>
internal class BuiltinCatalogConsts internal static class BuiltinCatalogConsts
{ {
/// <summary> /// <summary>
/// 文件极限大小100MB /// 文件极限大小100MB

View File

@@ -13,8 +13,12 @@ namespace YooAsset
#if UNITY_EDITOR #if UNITY_EDITOR
/// <summary> /// <summary>
/// 生成包裹的内置资源目录文件 /// 生成包裹的内置资源目录文件
/// 说明:根据指定目录下的文件生成清单文件 /// 说明:根据指定目录下的文件生成清单文件
/// </summary> /// </summary>
/// <param name="decryptor">资源清单解密器</param>
/// <param name="packageName">包裹名称</param>
/// <param name="packageDirectory">包裹在目录路径</param>
/// <returns>是否创建文件成功</returns>
public static bool CreateFile(IManifestDecryptor decryptor, string packageName, string packageDirectory) public static bool CreateFile(IManifestDecryptor decryptor, string packageName, string packageDirectory)
{ {
// 获取资源清单版本 // 获取资源清单版本
@@ -51,7 +55,7 @@ namespace YooAsset
{ {
foreach (var packageBundle in packageManifest.BundleList) foreach (var packageBundle in packageManifest.BundleList)
{ {
fileMapping.Add(packageBundle.FileName, packageBundle.BundleGuid); fileMapping.Add(packageBundle.GetFileName(), packageBundle.BundleGuid);
} }
} }
@@ -92,10 +96,10 @@ namespace YooAsset
continue; continue;
string fileName = fileInfo.Name; string fileName = fileInfo.Name;
if (fileMapping.TryGetValue(fileName, out string bundleGUID)) if (fileMapping.TryGetValue(fileName, out string bundleGuid))
{ {
var fileEntry = new BuiltinCatalog.CatalogEntry(); var fileEntry = new BuiltinCatalog.CatalogEntry();
fileEntry.BundleGUID = bundleGUID; fileEntry.BundleGuid = bundleGuid;
fileEntry.FileName = fileName; fileEntry.FileName = fileName;
buildinCatalog.Entries.Add(fileEntry); buildinCatalog.Entries.Add(fileEntry);
} }
@@ -125,6 +129,10 @@ namespace YooAsset
/// <summary> /// <summary>
/// 生成空的包裹内置资源目录文件 /// 生成空的包裹内置资源目录文件
/// </summary> /// </summary>
/// <param name="packageName">包裹名称</param>
/// <param name="packageVersion">包裹版本</param>
/// <param name="outputPath">输出目录路径</param>
/// <returns>是否创建文件成功</returns>
public static bool CreateEmptyFile(string packageName, string packageVersion, string outputPath) public static bool CreateEmptyFile(string packageName, string packageVersion, string outputPath)
{ {
// 创建内置清单实例 // 创建内置清单实例
@@ -153,6 +161,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 序列化为 JSON 文件 /// 序列化为 JSON 文件
/// </summary> /// </summary>
/// <param name="savePath">文件的保存路径</param>
/// <param name="catalog">要序列化的内置目录</param>
public static void SerializeToJson(string savePath, BuiltinCatalog catalog) public static void SerializeToJson(string savePath, BuiltinCatalog catalog)
{ {
string json = JsonUtility.ToJson(catalog, true); string json = JsonUtility.ToJson(catalog, true);
@@ -162,6 +172,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 序列化为二进制文件 /// 序列化为二进制文件
/// </summary> /// </summary>
/// <param name="savePath">文件的保存路径</param>
/// <param name="catalog">要序列化的内置目录</param>
public static void SerializeToBinary(string savePath, BuiltinCatalog catalog) public static void SerializeToBinary(string savePath, BuiltinCatalog catalog)
{ {
using (FileStream fs = new FileStream(savePath, FileMode.Create)) using (FileStream fs = new FileStream(savePath, FileMode.Create))
@@ -184,7 +196,7 @@ namespace YooAsset
for (int i = 0; i < catalog.Entries.Count; i++) for (int i = 0; i < catalog.Entries.Count; i++)
{ {
var fileWrapper = catalog.Entries[i]; var fileWrapper = catalog.Entries[i];
buffer.WriteString(fileWrapper.BundleGUID); buffer.WriteString(fileWrapper.BundleGuid);
buffer.WriteString(fileWrapper.FileName); buffer.WriteString(fileWrapper.FileName);
} }
@@ -198,6 +210,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从 JSON 反序列化 /// 从 JSON 反序列化
/// </summary> /// </summary>
/// <param name="jsonContent">文本内容</param>
/// <returns>反序列化得到的内置目录对象</returns>
public static BuiltinCatalog DeserializeFromJson(string jsonContent) public static BuiltinCatalog DeserializeFromJson(string jsonContent)
{ {
return JsonUtility.FromJson<BuiltinCatalog>(jsonContent); return JsonUtility.FromJson<BuiltinCatalog>(jsonContent);
@@ -206,6 +220,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 从二进制数据反序列化 /// 从二进制数据反序列化
/// </summary> /// </summary>
/// <param name="binaryData">二进制数据</param>
/// <returns>反序列化得到的内置目录对象</returns>
public static BuiltinCatalog DeserializeFromBinary(byte[] binaryData) public static BuiltinCatalog DeserializeFromBinary(byte[] binaryData)
{ {
if (binaryData == null || binaryData.Length == 0) if (binaryData == null || binaryData.Length == 0)
@@ -217,12 +233,12 @@ namespace YooAsset
// 读取文件标记 // 读取文件标记
uint fileMagic = buffer.ReadUInt32(); uint fileMagic = buffer.ReadUInt32();
if (fileMagic != BuiltinCatalogConsts.FileMagic) if (fileMagic != BuiltinCatalogConsts.FileMagic)
throw new Exception("Invalid catalog file."); throw new Exception("Catalog file is invalid.");
// 读取文件版本 // 读取文件版本
int fileVersion = buffer.ReadInt32(); int fileVersion = buffer.ReadInt32();
if (fileVersion != BuiltinCatalogConsts.FileVersion) if (fileVersion != BuiltinCatalogConsts.FileVersion)
throw new Exception($"The catalog file version is not compatible: {fileVersion} != {BuiltinCatalogConsts.FileVersion}."); throw new Exception($"Catalog file version is not compatible: {fileVersion} != {BuiltinCatalogConsts.FileVersion}.");
BuiltinCatalog catalog = new BuiltinCatalog(); BuiltinCatalog catalog = new BuiltinCatalog();
{ {
@@ -237,7 +253,7 @@ namespace YooAsset
for (int i = 0; i < fileCount; i++) for (int i = 0; i < fileCount; i++)
{ {
var fileEntry = new BuiltinCatalog.CatalogEntry(); var fileEntry = new BuiltinCatalog.CatalogEntry();
fileEntry.BundleGUID = buffer.ReadString(); fileEntry.BundleGuid = buffer.ReadString();
fileEntry.FileName = buffer.ReadString(); fileEntry.FileName = buffer.ReadString();
catalog.Entries.Add(fileEntry); catalog.Entries.Add(fileEntry);
} }

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 内置文件缓存初始化操作 /// 内置文件缓存初始化操作
/// </summary> /// </summary>
internal class BBCInitializeOperation : BCInitializeOperation internal sealed class BBCInitializeOperation : BCInitializeOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -18,6 +18,10 @@ namespace YooAsset
private LoadBuiltinCatalogOperation _loadBuiltinCatalogOp; private LoadBuiltinCatalogOperation _loadBuiltinCatalogOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建内置缓存初始化操作实例
/// </summary>
/// <param name="fileCache">内置文件缓存系统</param>
public BBCInitializeOperation(BuiltinBundleCache fileCache) public BBCInitializeOperation(BuiltinBundleCache fileCache)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -35,10 +39,10 @@ namespace YooAsset
{ {
if (_loadBuiltinCatalogOp == null) if (_loadBuiltinCatalogOp == null)
{ {
var options = new LoadBuiltinCatalogOptions(); var options = new LoadBuiltinCatalogOptions(
options.PackageName = _fileCache.PackageName; packageName: _fileCache.PackageName,
options.FilePath = _fileCache.GetCatalogBinaryFileLoadPath(); filePath: _fileCache.GetCatalogBinaryFileLoadPath(),
options.DownloadBackend = _fileCache.Config.DownloadBackend; downloadBackend: _fileCache.Config.DownloadBackend);
_loadBuiltinCatalogOp = new LoadBuiltinCatalogOperation(options); _loadBuiltinCatalogOp = new LoadBuiltinCatalogOperation(options);
_loadBuiltinCatalogOp.StartOperation(); _loadBuiltinCatalogOp.StartOperation();
AddChildOperation(_loadBuiltinCatalogOp); AddChildOperation(_loadBuiltinCatalogOp);
@@ -65,8 +69,8 @@ namespace YooAsset
foreach (var fileEntry in catalog.Entries) foreach (var fileEntry in catalog.Entries)
{ {
string filePath = PathUtility.Combine(_fileCache.RootPath, fileEntry.FileName); string filePath = PathUtility.Combine(_fileCache.RootPath, fileEntry.FileName);
var cacheEntry = new BuiltinBundleCacheEntry(fileEntry.BundleGUID, filePath); var cacheEntry = new BuiltinBundleCacheEntry(fileEntry.BundleGuid, filePath);
_fileCache.AddEntry(fileEntry.BundleGUID, cacheEntry); _fileCache.AddEntry(fileEntry.BundleGuid, cacheEntry);
} }
_steps = ESteps.Done; _steps = ESteps.Done;

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 内置文件缓存加载 AssetBundle 操作 /// 内置文件缓存加载 AssetBundle 操作
/// </summary> /// </summary>
internal class BBCLoadAssetBundleOperation : BCLoadBundleOperation internal sealed class BBCLoadAssetBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -20,6 +20,11 @@ namespace YooAsset
private BuiltinBundleCacheEntry _cacheEntry; private BuiltinBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建内置 AssetBundle 加载操作实例
/// </summary>
/// <param name="fileCache">内置文件缓存系统</param>
/// <param name="bundle">资源包描述</param>
public BBCLoadAssetBundleOperation(BuiltinBundleCache fileCache, PackageBundle bundle) public BBCLoadAssetBundleOperation(BuiltinBundleCache fileCache, PackageBundle bundle)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -52,11 +57,11 @@ namespace YooAsset
{ {
if (_loadLocalAssetBundleOp == null) if (_loadLocalAssetBundleOp == null)
{ {
var options = new LoadLocalAssetBundleOptions(); var options = new LoadLocalAssetBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _bundle; bundle: _bundle,
options.FilePath = _cacheEntry.FilePath; filePath: _cacheEntry.FilePath,
options.AssetBundleDecryptor = _fileCache.Config.AssetBundleDecryptor; assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor);
_loadLocalAssetBundleOp = new LoadLocalAssetBundleOperation(options); _loadLocalAssetBundleOp = new LoadLocalAssetBundleOperation(options);
_loadLocalAssetBundleOp.StartOperation(); _loadLocalAssetBundleOp.StartOperation();
AddChildOperation(_loadLocalAssetBundleOp); AddChildOperation(_loadLocalAssetBundleOp);
@@ -94,7 +99,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 内置文件缓存加载 RawBundle 操作 /// 内置文件缓存加载 RawBundle 操作
/// </summary> /// </summary>
internal class BBCLoadRawBundleOperation : BCLoadBundleOperation internal sealed class BBCLoadRawBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -142,11 +147,11 @@ namespace YooAsset
{ {
if(_loadLocalRawBundleOp == null) if(_loadLocalRawBundleOp == null)
{ {
var options = new LoadLocalRawBundleOptions(); var options = new LoadLocalRawBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _bundle; bundle: _bundle,
options.FilePath = _cacheEntry.FilePath; filePath: _cacheEntry.FilePath,
options.RawBundleDecryptor = _fileCache.Config.RawBundleDecryptor; rawBundleDecryptor: _fileCache.Config.RawBundleDecryptor);
_loadLocalRawBundleOp = new LoadLocalRawBundleOperation(options); _loadLocalRawBundleOp = new LoadLocalRawBundleOperation(options);
_loadLocalRawBundleOp.StartOperation(); _loadLocalRawBundleOp.StartOperation();
AddChildOperation(_loadLocalRawBundleOp); AddChildOperation(_loadLocalRawBundleOp);

View File

@@ -4,34 +4,42 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 编辑器文件缓存系统,用于编辑器模式下的资源模拟加载 /// 编辑器文件缓存系统,用于编辑器模式下的资源模拟加载
/// </summary> /// </summary>
internal class EditorBundleCache : IBundleCache internal class EditorBundleCache : IBundleCache
{ {
/// <summary> /// <summary>
/// 编辑器文件缓存配置 /// 编辑器文件缓存配置
/// </summary> /// </summary>
internal struct Configuration internal readonly struct Configuration
{ {
/// <summary> /// <summary>
/// 虚拟下载模式,模拟资源下载流程 /// 虚拟下载模式,模拟资源下载流程
/// </summary> /// </summary>
public bool VirtualDownloadMode { get; set; } public bool VirtualDownloadMode { get; }
/// <summary> /// <summary>
/// 虚拟WebGL模式 /// 虚拟WebGL模式
/// </summary> /// </summary>
public bool VirtualWebGLMode { get; set; } public bool VirtualWebGLMode { get; }
/// <summary> /// <summary>
/// 异步模拟最小帧数 /// 异步模拟最小帧数
/// </summary> /// </summary>
public int AsyncSimulateMinFrame { get; set; } public int AsyncSimulateMinFrame { get; }
/// <summary> /// <summary>
/// 异步模拟最大帧数 /// 异步模拟最大帧数
/// </summary> /// </summary>
public int AsyncSimulateMaxFrame { get; set; } public int AsyncSimulateMaxFrame { get; }
public Configuration(bool virtualDownloadMode, bool virtualWebGLMode, int asyncSimulateMinFrame, int asyncSimulateMaxFrame)
{
VirtualDownloadMode = virtualDownloadMode;
VirtualWebGLMode = virtualWebGLMode;
AsyncSimulateMinFrame = asyncSimulateMinFrame;
AsyncSimulateMaxFrame = asyncSimulateMaxFrame;
}
} }
private readonly Dictionary<string, EditorBundleCacheEntry> _cacheEntries = new Dictionary<string, EditorBundleCacheEntry>(10000); private readonly Dictionary<string, EditorBundleCacheEntry> _cacheEntries = new Dictionary<string, EditorBundleCacheEntry>(10000);
@@ -53,7 +61,7 @@ namespace YooAsset
public string RootPath { get; } public string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
public bool IsReadOnly { get; } public bool IsReadOnly { get; }
@@ -71,7 +79,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 已占用空间 /// 已占用空间
/// </summary> /// </summary>
/// <remarks>按缓存索引累计</remarks> /// <remarks>编辑器缓存为虚拟缓存,不占用磁盘空间。</remarks>
public long SpaceOccupied { get; private set; } public long SpaceOccupied { get; private set; }
#endregion #endregion
@@ -93,19 +101,19 @@ namespace YooAsset
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCInitializeOperation InitializeAsync() public BCInitializeOperation InitializeAsync()
{ {
var operation = new EBCInitializeOperation(this); var operation = new EBCInitializeOperation(this);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options) public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{ {
var operation = new EBCWriteCacheOperation(this, options); var operation = new EBCWriteCacheOperation(this, options);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options) public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{ {
ICacheEvictionPolicy policy = CreateEvictionPolicy(options); ICacheEvictionPolicy policy = CreateEvictionPolicy(options);
if (policy == null) if (policy == null)
@@ -117,7 +125,9 @@ namespace YooAsset
/// <summary> /// <summary>
/// 根据 ClearMethod 创建对应的淘汰策略实例 /// 根据 ClearMethod 创建对应的淘汰策略实例
/// </summary> /// </summary>
protected virtual ICacheEvictionPolicy CreateEvictionPolicy(BCClearCacheOptions options) /// <param name="options">清理缓存选项</param>
/// <returns>淘汰策略实例</returns>
protected ICacheEvictionPolicy CreateEvictionPolicy(BCClearCacheOptions options)
{ {
if (options.ClearMethod == ClearCacheMethods.ClearAllBundleFiles) if (options.ClearMethod == ClearCacheMethods.ClearAllBundleFiles)
return new EvictionAllPolicy(); return new EvictionAllPolicy();
@@ -134,31 +144,31 @@ namespace YooAsset
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options) public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{ {
var operation = new BCVerifyCacheCompleteOperation(); var operation = new BCVerifyCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options) public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{ {
if (options.Bundle.BundleType == (int)EBundleType.VirtualBundle) if (options.Bundle.GetBundleType() == (int)EBundleType.VirtualBundle)
{ {
var operation = new EBCLoadBundleOperation(this, options.Bundle); var operation = new EBCLoadBundleOperation(this, options.Bundle);
return operation; return operation;
} }
else else
{ {
string error = $"{nameof(EditorBundleCache)} does not support bundle type: {options.Bundle.BundleType}."; string error = $"{nameof(EditorBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error); var operation = new BCLoadBundleErrorOperation(error);
return operation; return operation;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsCached(string bundleGUID) public bool IsCached(string bundleGuid)
{ {
if (Config.VirtualDownloadMode) if (Config.VirtualDownloadMode)
return _cacheEntries.ContainsKey(bundleGUID); return _cacheEntries.ContainsKey(bundleGuid);
else else
return true; return true;
} }
@@ -167,6 +177,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取所有缓存条目 /// 获取所有缓存条目
/// </summary> /// </summary>
/// <returns>当前字典中全部缓存条目的只读集合</returns>
internal IReadOnlyCollection<EditorBundleCacheEntry> GetAllEntries() internal IReadOnlyCollection<EditorBundleCacheEntry> GetAllEntries()
{ {
return _cacheEntries.Values; return _cacheEntries.Values;
@@ -175,22 +186,25 @@ namespace YooAsset
/// <summary> /// <summary>
/// 添加指定缓存条目 /// 添加指定缓存条目
/// </summary> /// </summary>
internal void AddEntry(string bundleGUID, EditorBundleCacheEntry cacheEntry) /// <param name="bundleGuid">资源包 GUID</param>
/// <param name="cacheEntry">编辑器缓存条目</param>
internal void AddEntry(string bundleGuid, EditorBundleCacheEntry cacheEntry)
{ {
if (_cacheEntries.ContainsKey(bundleGUID)) if (_cacheEntries.ContainsKey(bundleGuid))
throw new YooInternalException($"Cache entry already exists: '{bundleGUID}'."); throw new YooInternalException($"Cache entry already exists: '{bundleGuid}'.");
_cacheEntries.Add(bundleGUID, cacheEntry); _cacheEntries.Add(bundleGuid, cacheEntry);
} }
/// <summary> /// <summary>
/// 删除指定缓存条目 /// 删除指定缓存条目
/// </summary> /// </summary>
internal void RemoveEntry(string bundleGUID) /// <param name="bundleGuid">资源包 GUID</param>
internal void RemoveEntry(string bundleGuid)
{ {
if (_cacheEntries.TryGetValue(bundleGUID, out EditorBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundleGuid, out EditorBundleCacheEntry entry))
{ {
_cacheEntries.Remove(bundleGUID); _cacheEntries.Remove(bundleGuid);
} }
} }
#endregion #endregion

View File

@@ -9,7 +9,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 资源包文件路径 /// 资源包文件路径
@@ -17,13 +17,13 @@ namespace YooAsset
public string FilePath { get; private set; } public string FilePath { get; private set; }
/// <summary> /// <summary>
/// 创建编辑器文件缓存条目 /// 创建编辑器文件缓存条目实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="filePath">资源包文件路径</param> /// <param name="filePath">资源包文件路径</param>
public EditorBundleCacheEntry(string bundleGUID, string filePath) public EditorBundleCacheEntry(string bundleGuid, string filePath)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
FilePath = filePath; FilePath = filePath;
} }
} }

View File

@@ -18,9 +18,15 @@ namespace YooAsset
private readonly EditorBundleCache _fileCache; private readonly EditorBundleCache _fileCache;
private readonly BCClearCacheOptions _options; private readonly BCClearCacheOptions _options;
private readonly ICacheEvictionPolicy _policy; private readonly ICacheEvictionPolicy _policy;
private List<string> _bundleGUIDs; private IReadOnlyList<string> _bundleGuids;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建编辑器清理缓存操作实例
/// </summary>
/// <param name="fileCache">编辑器文件缓存系统</param>
/// <param name="options">清理缓存选项</param>
/// <param name="policy">缓存逐出策略</param>
internal EBCClearCacheOperation(EditorBundleCache fileCache, BCClearCacheOptions options, ICacheEvictionPolicy policy) internal EBCClearCacheOperation(EditorBundleCache fileCache, BCClearCacheOptions options, ICacheEvictionPolicy policy)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -48,15 +54,15 @@ namespace YooAsset
return; return;
} }
_bundleGUIDs = clearResult.BundleGUIDs; _bundleGuids = clearResult.BundleGuids;
_steps = ESteps.ClearCacheFiles; _steps = ESteps.ClearCacheFiles;
} }
if (_steps == ESteps.ClearCacheFiles) if (_steps == ESteps.ClearCacheFiles)
{ {
foreach (var bundleGUID in _bundleGUIDs) foreach (var bundleGuid in _bundleGuids)
{ {
_fileCache.RemoveEntry(bundleGUID); _fileCache.RemoveEntry(bundleGuid);
} }
_steps = ESteps.Done; _steps = ESteps.Done;

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 编辑器文件缓存初始化操作 /// 编辑器文件缓存初始化操作
/// </summary> /// </summary>
internal class EBCInitializeOperation : BCInitializeOperation internal sealed class EBCInitializeOperation : BCInitializeOperation
{ {
private readonly EditorBundleCache _fileCache; private readonly EditorBundleCache _fileCache;

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 编辑器文件缓存加载资源包操作 /// 编辑器文件缓存加载资源包操作
/// </summary> /// </summary>
internal class EBCLoadBundleOperation : BCLoadBundleOperation internal sealed class EBCLoadBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 编辑器文件缓存写入操作 /// 编辑器文件缓存写入操作
/// </summary> /// </summary>
internal class EBCWriteCacheOperation : BCWriteCacheOperation internal sealed class EBCWriteCacheOperation : BCWriteCacheOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -18,9 +18,14 @@ namespace YooAsset
private readonly BCWriteCacheOptions _options; private readonly BCWriteCacheOptions _options;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public EBCWriteCacheOperation(EditorBundleCache cache, BCWriteCacheOptions options) /// <summary>
/// 创建编辑器写入缓存操作实例
/// </summary>
/// <param name="fileCache">编辑器文件缓存系统</param>
/// <param name="options">写入缓存选项</param>
public EBCWriteCacheOperation(EditorBundleCache fileCache, BCWriteCacheOptions options)
{ {
_fileCache = cache; _fileCache = fileCache;
_options = options; _options = options;
} }
protected override void InternalStart() protected override void InternalStart()
@@ -37,7 +42,7 @@ namespace YooAsset
if (_fileCache.IsCached(_options.Bundle.BundleGuid)) if (_fileCache.IsCached(_options.Bundle.BundleGuid))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError("The bundle is already cached."); SetError("Bundle is already cached.");
} }
else else
{ {

View File

@@ -13,9 +13,9 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="filePath">文件路径</param> /// <param name="filePath">文件路径</param>
/// <param name="fileSize">期望的文件大小</param> /// <param name="fileSize">期望的文件大小</param>
/// <param name="fileCRC">期望的文件CRC值</param> /// <param name="fileCrc">期望的文件CRC值</param>
/// <returns>校验结果</returns> /// <returns>校验结果</returns>
public static EFileVerifyResult VerifyFile(string filePath, long fileSize, uint fileCRC) public static EFileVerifyResult VerifyFile(string filePath, long fileSize, uint fileCrc)
{ {
try try
{ {
@@ -23,6 +23,7 @@ namespace YooAsset
return EFileVerifyResult.DataFileNotExisted; return EFileVerifyResult.DataFileNotExisted;
// 可选条件:验证文件大小 // 可选条件:验证文件大小
// fileSize == 0 表示调用方不要求大小校验(哨兵值)
if (fileSize > 0) if (fileSize > 0)
{ {
long size = FileUtility.GetFileSize(filePath); long size = FileUtility.GetFileSize(filePath);
@@ -33,10 +34,11 @@ namespace YooAsset
} }
// 可选条件验证文件CRC // 可选条件验证文件CRC
if (fileCRC > 0) // fileCRC == 0 表示调用方不要求 CRC 校验(哨兵值)
if (fileCrc > 0)
{ {
uint crc = HashUtility.ComputeFileCrc32AsUInt(filePath); uint crc = HashUtility.ComputeFileCrc32AsUInt(filePath);
if (crc == fileCRC) if (crc == fileCrc)
return EFileVerifyResult.Succeed; return EFileVerifyResult.Succeed;
else else
return EFileVerifyResult.DataFileCrcError; return EFileVerifyResult.DataFileCrcError;
@@ -48,7 +50,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"File verification exception: {ex.Message}."); YooLogger.LogError($"File verification exception: {ex.Message}.");
return EFileVerifyResult.Exception; return EFileVerifyResult.Exception;
} }
} }
@@ -58,9 +60,9 @@ namespace YooAsset
/// </summary> /// </summary>
/// <param name="fileData">文件数据</param> /// <param name="fileData">文件数据</param>
/// <param name="fileSize">期望的文件大小</param> /// <param name="fileSize">期望的文件大小</param>
/// <param name="fileCRC">期望的文件CRC值</param> /// <param name="fileCrc">期望的文件CRC值</param>
/// <returns>校验结果</returns> /// <returns>校验结果</returns>
public static EFileVerifyResult VerifyFile(byte[] fileData, long fileSize, uint fileCRC) public static EFileVerifyResult VerifyFile(byte[] fileData, long fileSize, uint fileCrc)
{ {
try try
{ {
@@ -68,6 +70,7 @@ namespace YooAsset
return EFileVerifyResult.DataFileInvalid; return EFileVerifyResult.DataFileInvalid;
// 可选条件:验证文件大小 // 可选条件:验证文件大小
// fileSize == 0 表示调用方不要求大小校验(哨兵值)
if (fileSize > 0) if (fileSize > 0)
{ {
long size = fileData.Length; long size = fileData.Length;
@@ -78,10 +81,11 @@ namespace YooAsset
} }
// 可选条件验证文件CRC // 可选条件验证文件CRC
if (fileCRC > 0) // fileCRC == 0 表示调用方不要求 CRC 校验(哨兵值)
if (fileCrc > 0)
{ {
uint crc = HashUtility.ComputeCrc32AsUInt(fileData); uint crc = HashUtility.ComputeCrc32AsUInt(fileData);
if (crc == fileCRC) if (crc == fileCrc)
return EFileVerifyResult.Succeed; return EFileVerifyResult.Succeed;
else else
return EFileVerifyResult.DataFileCrcError; return EFileVerifyResult.DataFileCrcError;
@@ -93,7 +97,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"File verification exception: {ex.Message}."); YooLogger.LogError($"File verification exception: {ex.Message}.");
return EFileVerifyResult.Exception; return EFileVerifyResult.Exception;
} }
} }

View File

@@ -5,7 +5,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 清理缓存文件操作 /// 清理缓存文件操作
/// </summary> /// </summary>
internal class ClearCacheFilesOperation : AsyncOperationBase internal sealed class ClearCacheFilesOperation : AsyncOperationBase
{ {
private enum ESteps private enum ESteps
{ {
@@ -16,14 +16,19 @@ namespace YooAsset
} }
private readonly SandboxBundleCache _fileCache; private readonly SandboxBundleCache _fileCache;
private readonly List<string> _bundleGUIDs; private readonly List<string> _bundleGuids;
private int _fileTotalCount; private int _fileTotalCount;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public ClearCacheFilesOperation(SandboxBundleCache fileCache, List<string> bundleGUIDs) /// <summary>
/// 创建清理缓存文件操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="bundleGuids">要清理的资源包 GUID 列表</param>
public ClearCacheFilesOperation(SandboxBundleCache fileCache, List<string> bundleGuids)
{ {
_fileCache = fileCache; _fileCache = fileCache;
_bundleGUIDs = bundleGUIDs; _bundleGuids = bundleGuids;
} }
protected override void InternalStart() protected override void InternalStart()
{ {
@@ -36,24 +41,24 @@ namespace YooAsset
if (_steps == ESteps.CheckParam) if (_steps == ESteps.CheckParam)
{ {
if (_bundleGUIDs == null || _bundleGUIDs.Count == 0) if (_bundleGuids == null || _bundleGuids.Count == 0)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetResult(); SetResult();
return; return;
} }
_fileTotalCount = _bundleGUIDs.Count; _fileTotalCount = _bundleGuids.Count;
_steps = ESteps.ClearCache; _steps = ESteps.ClearCache;
} }
if (_steps == ESteps.ClearCache) if (_steps == ESteps.ClearCache)
{ {
for (int i = _bundleGUIDs.Count - 1; i >= 0; i--) for (int i = _bundleGuids.Count - 1; i >= 0; i--)
{ {
string bundleGUID = _bundleGUIDs[i]; string bundleGuid = _bundleGuids[i];
_fileCache.RemoveEntry(bundleGUID); _fileCache.RemoveEntry(bundleGuid);
_bundleGUIDs.RemoveAt(i); _bundleGuids.RemoveAt(i);
if (IsBusy) if (IsBusy)
break; break;
} }
@@ -61,9 +66,9 @@ namespace YooAsset
if (_fileTotalCount == 0) if (_fileTotalCount == 0)
Progress = 1.0f; Progress = 1.0f;
else else
Progress = 1.0f - ((float)_bundleGUIDs.Count / _fileTotalCount); Progress = 1.0f - ((float)_bundleGuids.Count / _fileTotalCount);
if (_bundleGUIDs.Count == 0) if (_bundleGuids.Count == 0)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetResult(); SetResult();

View File

@@ -6,7 +6,7 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 搜索缓存文件操作,扫描缓存目录中的文件 /// 搜索缓存文件操作,扫描缓存目录中的文件
/// </summary> /// </summary>
internal sealed class SearchCacheFilesOperation : AsyncOperationBase internal sealed class SearchCacheFilesOperation : AsyncOperationBase
{ {
@@ -28,7 +28,10 @@ namespace YooAsset
/// </summary> /// </summary>
public readonly List<SearchFileInfo> Result = new List<SearchFileInfo>(5000); public readonly List<SearchFileInfo> Result = new List<SearchFileInfo>(5000);
/// <summary>
/// 创建搜索缓存文件操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
internal SearchCacheFilesOperation(SandboxBundleCache fileCache) internal SearchCacheFilesOperation(SandboxBundleCache fileCache)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -69,7 +72,15 @@ namespace YooAsset
_steps = ESteps.Done; _steps = ESteps.Done;
SetResult(); SetResult();
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime; double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Search cache files elapsed time {costTime:f1} seconds"); YooLogger.Log($"Cache file search completed in {costTime:f1} seconds.");
}
}
protected override void InternalDispose()
{
if (_filesEnumerator != null)
{
_filesEnumerator.Dispose();
_filesEnumerator = null;
} }
} }
@@ -86,15 +97,15 @@ namespace YooAsset
var childDirectories = Directory.EnumerateDirectories(rootFolder); var childDirectories = Directory.EnumerateDirectories(rootFolder);
foreach (var childDirectory in childDirectories) foreach (var childDirectory in childDirectories)
{ {
string bundleGUID = Path.GetFileName(childDirectory); string bundleGuid = Path.GetFileName(childDirectory);
if (_fileCache.IsCached(bundleGUID)) if (_fileCache.IsCached(bundleGuid))
continue; continue;
// 创建验证元素类 // 创建验证元素类
string fileRootPath = childDirectory; string fileRootPath = childDirectory;
string dataFilePath = PathUtility.Combine(fileRootPath, SandboxBundleCacheConsts.BundleDataFileName); string dataFilePath = PathUtility.Combine(fileRootPath, SandboxBundleCacheConsts.BundleDataFileName);
string infoFilePath = PathUtility.Combine(fileRootPath, SandboxBundleCacheConsts.BundleInfoFileName); string infoFilePath = PathUtility.Combine(fileRootPath, SandboxBundleCacheConsts.BundleInfoFileName);
var element = new SearchFileInfo(bundleGUID, fileRootPath, dataFilePath, infoFilePath); var element = new SearchFileInfo(bundleGuid, fileRootPath, dataFilePath, infoFilePath);
Result.Add(element); Result.Add(element);
} }

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
@@ -7,7 +6,7 @@ using System.Threading;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 缓存文件验证(线程版),验证缓存目录中的文件 /// 缓存文件验证(线程版),验证缓存目录中的文件
/// </summary> /// </summary>
internal sealed class VerifyCacheFilesOperation : AsyncOperationBase internal sealed class VerifyCacheFilesOperation : AsyncOperationBase
{ {
@@ -31,7 +30,13 @@ namespace YooAsset
private int _failedCount; private int _failedCount;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建缓存文件验证操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="verifyLevel">文件校验等级</param>
/// <param name="fileVerifyMaxConcurrency">文件校验最大并发数</param>
/// <param name="elements">待验证的搜索文件信息列表</param>
internal VerifyCacheFilesOperation(SandboxBundleCache fileCache, EFileVerifyLevel verifyLevel, int fileVerifyMaxConcurrency, List<SearchFileInfo> elements) internal VerifyCacheFilesOperation(SandboxBundleCache fileCache, EFileVerifyLevel verifyLevel, int fileVerifyMaxConcurrency, List<SearchFileInfo> elements)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -56,7 +61,7 @@ namespace YooAsset
if (_maxConcurrentVerifyCount < 1) if (_maxConcurrentVerifyCount < 1)
_maxConcurrentVerifyCount = 1; _maxConcurrentVerifyCount = 1;
YooLogger.Log($"Verify max concurrency: {_maxConcurrentVerifyCount}"); YooLogger.Log($"Maximum verify concurrency is {_maxConcurrentVerifyCount}.");
_activeVerifyList = new List<SearchFileInfo>(_maxConcurrentVerifyCount); _activeVerifyList = new List<SearchFileInfo>(_maxConcurrentVerifyCount);
_verifyStartTime = TimeUtility.RealtimeSinceStartup; _verifyStartTime = TimeUtility.RealtimeSinceStartup;
_verifyTotalCount = _pendingVerifyList.Count; _verifyTotalCount = _pendingVerifyList.Count;
@@ -76,13 +81,13 @@ namespace YooAsset
if (resultCode == (int)EFileVerifyResult.Succeed) if (resultCode == (int)EFileVerifyResult.Succeed)
{ {
_successCount++; _successCount++;
var cacheEntry = new SandboxBundleCacheEntry(verifyElement.BundleGUID, verifyElement.InfoFilePath, verifyElement.DataFilePath); var cacheEntry = new SandboxBundleCacheEntry(verifyElement.BundleGuid, verifyElement.InfoFilePath, verifyElement.DataFilePath);
_fileCache.AddEntry(verifyElement.BundleGUID, cacheEntry); _fileCache.AddEntry(verifyElement.BundleGuid, cacheEntry);
} }
else else
{ {
_failedCount++; _failedCount++;
YooLogger.Warning($"File verification failed (code: {verifyElement.VerifyResultCode}). Deleting files: '{verifyElement.FolderPath}'."); YooLogger.LogWarning($"File verification failed (code: {resultCode}). Deleting files: '{verifyElement.FolderPath}'.");
verifyElement.DeleteCacheFolder(); verifyElement.DeleteCacheFolder();
} }
} }
@@ -94,7 +99,7 @@ namespace YooAsset
_steps = ESteps.Done; _steps = ESteps.Done;
SetResult(); SetResult();
double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime; double costTime = TimeUtility.RealtimeSinceStartup - _verifyStartTime;
YooLogger.Log($"Verify cache files elapsed time {costTime:f1} seconds"); YooLogger.Log($"Cache file verification completed in {costTime:f1} seconds.");
} }
for (int i = _pendingVerifyList.Count - 1; i >= 0; i--) for (int i = _pendingVerifyList.Count - 1; i >= 0; i--)
@@ -163,11 +168,11 @@ namespace YooAsset
else if (verifyLevel == EFileVerifyLevel.High) else if (verifyLevel == EFileVerifyLevel.High)
return FileVerifyHelper.VerifyFile(element.DataFilePath, dataFileSize, dataFileCRC); return FileVerifyHelper.VerifyFile(element.DataFilePath, dataFileSize, dataFileCRC);
else else
throw new System.NotImplementedException(verifyLevel.ToString()); throw new YooInternalException($"Unexpected verify level: {verifyLevel}.");
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"File verification exception: {ex.Message}."); YooLogger.LogError($"File verification exception: {ex.Message}.");
return EFileVerifyResult.Exception; return EFileVerifyResult.Exception;
} }
} }

View File

@@ -24,7 +24,10 @@ namespace YooAsset
/// </summary> /// </summary>
public EFileVerifyResult VerifyResult { private set; get; } public EFileVerifyResult VerifyResult { private set; get; }
/// <summary>
/// 创建下载文件验证(线程版)实例
/// </summary>
/// <param name="element">临时文件信息</param>
internal VerifyTempFileOperation(TempFileInfo element) internal VerifyTempFileOperation(TempFileInfo element)
{ {
_element = element; _element = element;
@@ -75,7 +78,7 @@ namespace YooAsset
private void VerifyFileInThread(object obj) private void VerifyFileInThread(object obj)
{ {
TempFileInfo element = (TempFileInfo)obj; TempFileInfo element = (TempFileInfo)obj;
int resultCode = (int)FileVerifyHelper.VerifyFile(element.FilePath, element.FileSize, element.FileCRC); int resultCode = (int)FileVerifyHelper.VerifyFile(element.FilePath, element.FileSize, element.FileCrc);
element.VerifyResultCode = resultCode; //注意: 一次命令赋值 element.VerifyResultCode = resultCode; //注意: 一次命令赋值
} }
} }

View File

@@ -1,3 +1,4 @@
using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
@@ -20,6 +21,12 @@ namespace YooAsset
private ClearCacheFilesOperation _clearCacheFilesOp; private ClearCacheFilesOperation _clearCacheFilesOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建沙盒清理缓存操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="options">清理缓存选项</param>
/// <param name="policy">缓存淘汰策略</param>
internal SBCClearCacheOperation(SandboxBundleCache fileCache, BCClearCacheOptions options, ICacheEvictionPolicy policy) internal SBCClearCacheOperation(SandboxBundleCache fileCache, BCClearCacheOptions options, ICacheEvictionPolicy policy)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -48,7 +55,7 @@ namespace YooAsset
return; return;
} }
_clearCacheFilesOp = new ClearCacheFilesOperation(_fileCache, clearResult.BundleGUIDs); _clearCacheFilesOp = new ClearCacheFilesOperation(_fileCache, new List<string>(clearResult.BundleGuids));
_clearCacheFilesOp.StartOperation(); _clearCacheFilesOp.StartOperation();
AddChildOperation(_clearCacheFilesOp); AddChildOperation(_clearCacheFilesOp);
_steps = ESteps.ClearCacheFiles; _steps = ESteps.ClearCacheFiles;

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 沙盒文件缓存初始化操作 /// 沙盒文件缓存初始化操作
/// </summary> /// </summary>
internal class SBCInitializeOperation : BCInitializeOperation internal sealed class SBCInitializeOperation : BCInitializeOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -19,6 +19,10 @@ namespace YooAsset
private VerifyCacheFilesOperation _verifyCacheFilesOp; private VerifyCacheFilesOperation _verifyCacheFilesOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建沙盒缓存初始化操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
public SBCInitializeOperation(SandboxBundleCache fileCache) public SBCInitializeOperation(SandboxBundleCache fileCache)
{ {
_fileCache = fileCache; _fileCache = fileCache;

View File

@@ -7,7 +7,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 沙盒文件缓存加载 AssetBundle 操作 /// 沙盒文件缓存加载 AssetBundle 操作
/// </summary> /// </summary>
internal class SBCLoadAssetBundleOperation : BCLoadBundleOperation internal sealed class SBCLoadAssetBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -26,10 +26,15 @@ namespace YooAsset
private SandboxBundleCacheEntry _cacheEntry; private SandboxBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public SBCLoadAssetBundleOperation(SandboxBundleCache fileCache, PackageBundle bundle) /// <summary>
/// 创建沙盒 AssetBundle 加载操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="packageBundle">资源包描述</param>
public SBCLoadAssetBundleOperation(SandboxBundleCache fileCache, PackageBundle packageBundle)
{ {
_fileCache = fileCache; _fileCache = fileCache;
_bundle = bundle; _bundle = packageBundle;
} }
protected override void InternalStart() protected override void InternalStart()
{ {
@@ -58,11 +63,11 @@ namespace YooAsset
{ {
if (_loadLocalAssetBundleOp == null) if (_loadLocalAssetBundleOp == null)
{ {
var options = new LoadLocalAssetBundleOptions(); var options = new LoadLocalAssetBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _bundle; bundle: _bundle,
options.FilePath = _cacheEntry.DataFilePath; filePath: _cacheEntry.DataFilePath,
options.AssetBundleDecryptor = _fileCache.Config.AssetBundleDecryptor; assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor);
_loadLocalAssetBundleOp = new LoadLocalAssetBundleOperation(options); _loadLocalAssetBundleOp = new LoadLocalAssetBundleOperation(options);
_loadLocalAssetBundleOp.StartOperation(); _loadLocalAssetBundleOp.StartOperation();
AddChildOperation(_loadLocalAssetBundleOp); AddChildOperation(_loadLocalAssetBundleOp);
@@ -105,9 +110,7 @@ namespace YooAsset
// 说明在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性 // 说明在AssetBundle文件加载失败的情况下我们需要重新验证文件的完整性
if (_verifyCacheOp == null) if (_verifyCacheOp == null)
{ {
var options = new BCVerifyCacheOptions(); var options = new BCVerifyCacheOptions(_bundle, true);
options.Bundle = _bundle;
options.DeleteCacheEntryOnFailure = true;
_verifyCacheOp = _fileCache.VerifyCacheAsync(options); _verifyCacheOp = _fileCache.VerifyCacheAsync(options);
_verifyCacheOp.StartOperation(); _verifyCacheOp.StartOperation();
AddChildOperation(_verifyCacheOp); AddChildOperation(_verifyCacheOp);
@@ -177,24 +180,40 @@ namespace YooAsset
} }
private AssetBundle FallbackLoadAssetBundle() private AssetBundle FallbackLoadAssetBundle()
{
try
{ {
byte[] fileData = FileUtility.ReadAllBytes(_cacheEntry.DataFilePath); byte[] fileData = FileUtility.ReadAllBytes(_cacheEntry.DataFilePath);
return AssetBundle.LoadFromMemory(fileData); return AssetBundle.LoadFromMemory(fileData);
} }
catch (Exception ex)
{
YooLogger.LogWarning($"Fallback load failed: {ex.Message}.");
return null;
}
}
private AssetBundle FallbackLoadEncryptedAssetBundle(IBundleMemoryDecryptor decryptor) private AssetBundle FallbackLoadEncryptedAssetBundle(IBundleMemoryDecryptor decryptor)
{ {
var args = new BundleDecryptArgs(); try
args.Bundle = _bundle; {
args.FilePath = _cacheEntry.DataFilePath; var args = new BundleDecryptArgs(_bundle, null, _cacheEntry.DataFilePath);
var fileData = decryptor.GetDecryptedData(args); var fileData = decryptor.GetDecryptedData(args);
if (fileData == null)
return null;
return AssetBundle.LoadFromMemory(fileData); return AssetBundle.LoadFromMemory(fileData);
} }
catch (Exception ex)
{
YooLogger.LogWarning($"Fallback encrypted load failed: {ex.Message}.");
return null;
}
}
} }
/// <summary> /// <summary>
/// 沙盒文件缓存加载 RawBundle 操作 /// 沙盒文件缓存加载 RawBundle 操作
/// </summary> /// </summary>
internal class SBCLoadRawBundleOperation : BCLoadBundleOperation internal sealed class SBCLoadRawBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -211,6 +230,11 @@ namespace YooAsset
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建沙盒 RawBundle 加载操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="bundle">资源包描述</param>
public SBCLoadRawBundleOperation(SandboxBundleCache fileCache, PackageBundle bundle) public SBCLoadRawBundleOperation(SandboxBundleCache fileCache, PackageBundle bundle)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -243,11 +267,11 @@ namespace YooAsset
{ {
if (_loadLocalRawBundleOp == null) if (_loadLocalRawBundleOp == null)
{ {
var options = new LoadLocalRawBundleOptions(); var options = new LoadLocalRawBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _bundle; bundle: _bundle,
options.FilePath = _cacheEntry.DataFilePath; filePath: _cacheEntry.DataFilePath,
options.RawBundleDecryptor = _fileCache.Config.RawBundleDecryptor; rawBundleDecryptor: _fileCache.Config.RawBundleDecryptor);
_loadLocalRawBundleOp = new LoadLocalRawBundleOperation(options); _loadLocalRawBundleOp = new LoadLocalRawBundleOperation(options);
_loadLocalRawBundleOp.StartOperation(); _loadLocalRawBundleOp.StartOperation();
AddChildOperation(_loadLocalRawBundleOp); AddChildOperation(_loadLocalRawBundleOp);
@@ -283,7 +307,7 @@ namespace YooAsset
} }
#if TUANJIE_1_7_OR_NEWER #if TUANJIE_1_7_OR_NEWER
internal class SBCLoadInstantBundleOperation : BCLoadBundleOperation internal sealed class SBCLoadInstantBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -295,11 +319,11 @@ namespace YooAsset
protected override void InternalStart() protected override void InternalStart()
{ {
throw new NotImplementedException(); throw new NotImplementedException($"{nameof(SBCLoadInstantBundleOperation)} is not implemented.");
} }
protected override void InternalUpdate() protected override void InternalUpdate()
{ {
throw new NotImplementedException(); throw new NotImplementedException($"{nameof(SBCLoadInstantBundleOperation)} is not implemented.");
} }
} }
#endif #endif

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 沙盒文件缓存验证操作 /// 沙盒文件缓存验证操作
/// </summary> /// </summary>
internal class SBCVerifyCacheOperation : BCVerifyCacheOperation internal sealed class SBCVerifyCacheOperation : BCVerifyCacheOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -19,9 +19,14 @@ namespace YooAsset
private VerifyTempFileOperation _verifyTempFileOp; private VerifyTempFileOperation _verifyTempFileOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public SBCVerifyCacheOperation(SandboxBundleCache cache, BCVerifyCacheOptions options) /// <summary>
/// 创建沙盒验证缓存操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="options">验证缓存选项</param>
public SBCVerifyCacheOperation(SandboxBundleCache fileCache, BCVerifyCacheOptions options)
{ {
_fileCache = cache; _fileCache = fileCache;
_options = options; _options = options;
} }
protected override void InternalStart() protected override void InternalStart()
@@ -38,7 +43,7 @@ namespace YooAsset
if (_fileCache.IsCached(_options.Bundle.BundleGuid) == false) if (_fileCache.IsCached(_options.Bundle.BundleGuid) == false)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError("Cached bundle not found."); SetError($"File cache entry not found: '{_options.Bundle.BundleGuid}'.");
} }
else else
{ {
@@ -51,6 +56,13 @@ namespace YooAsset
if (_verifyTempFileOp == null) if (_verifyTempFileOp == null)
{ {
var entry = _fileCache.GetEntry(_options.Bundle.BundleGuid); var entry = _fileCache.GetEntry(_options.Bundle.BundleGuid);
if (entry == null)
{
_steps = ESteps.Done;
SetError($"File cache entry not found: '{_options.Bundle.BundleGuid}'.");
return;
}
var element = new TempFileInfo(entry.DataFilePath, _options.Bundle.FileCrc, _options.Bundle.FileSize); var element = new TempFileInfo(entry.DataFilePath, _options.Bundle.FileCrc, _options.Bundle.FileSize);
_verifyTempFileOp = new VerifyTempFileOperation(element); _verifyTempFileOp = new VerifyTempFileOperation(element);
_verifyTempFileOp.StartOperation(); _verifyTempFileOp.StartOperation();
@@ -76,7 +88,7 @@ namespace YooAsset
if (_options.DeleteCacheEntryOnFailure) if (_options.DeleteCacheEntryOnFailure)
{ {
YooLogger.Error($"Found corrupted bundle file. Removing cache entry: '{_options.Bundle.BundleGuid}'."); YooLogger.LogError($"Found corrupted bundle file. Removing cache entry: '{_options.Bundle.BundleGuid}'.");
_fileCache.RemoveEntry(_options.Bundle.BundleGuid); _fileCache.RemoveEntry(_options.Bundle.BundleGuid);
} }
} }

View File

@@ -6,7 +6,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 沙盒文件缓存写入操作 /// 沙盒文件缓存写入操作
/// </summary> /// </summary>
internal class SBCWriteCacheOperation : BCWriteCacheOperation internal sealed class SBCWriteCacheOperation : BCWriteCacheOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -22,6 +22,11 @@ namespace YooAsset
private VerifyTempFileOperation _verifyTempFileOp; private VerifyTempFileOperation _verifyTempFileOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建沙盒写入缓存操作实例
/// </summary>
/// <param name="fileCache">沙盒文件缓存系统</param>
/// <param name="options">写入缓存选项</param>
public SBCWriteCacheOperation(SandboxBundleCache fileCache, BCWriteCacheOptions options) public SBCWriteCacheOperation(SandboxBundleCache fileCache, BCWriteCacheOptions options)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -41,7 +46,7 @@ namespace YooAsset
if (_fileCache.IsCached(_options.Bundle.BundleGuid)) if (_fileCache.IsCached(_options.Bundle.BundleGuid))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError("The bundle is already cached."); SetError("Bundle is already cached.");
} }
else else
{ {
@@ -120,8 +125,8 @@ namespace YooAsset
catch (Exception ex) catch (Exception ex)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Failed to write cache file. Error: {ex.Message}."); SetError($"Failed to write cache file: {ex.Message}.");
YooLogger.Error(Error); YooLogger.LogError(Error);
// 回滚:清理临时文件,正式文件不受影响 // 回滚:清理临时文件,正式文件不受影响
DeleteFileSafely(dataTempPath); DeleteFileSafely(dataTempPath);
@@ -150,7 +155,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Warning($"Failed to delete file '{filePath}'. Error: {ex.Message}."); YooLogger.LogWarning($"Failed to delete file '{filePath}': {ex.Message}.");
} }
} }
} }

View File

@@ -4,39 +4,49 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 沙盒文件缓存系统,用于管理下载到本地的资源包缓存 /// 沙盒文件缓存系统,用于管理下载到本地的资源包缓存
/// </summary> /// </summary>
internal class SandboxBundleCache : IBundleCache internal class SandboxBundleCache : IBundleCache
{ {
/// <summary> /// <summary>
/// 沙盒文件缓存配置 /// 沙盒文件缓存配置
/// </summary> /// </summary>
internal struct Configuration internal readonly struct Configuration
{ {
/// <summary> /// <summary>
/// 文件校验最大并发数 /// 文件校验最大并发数
/// </summary> /// </summary>
public int FileVerifyMaxConcurrency { get; set; } public int FileVerifyMaxConcurrency { get; }
/// <summary> /// <summary>
/// 文件校验级别 /// 文件校验级别
/// </summary> /// </summary>
public EFileVerifyLevel FileVerifyLevel { get; set; } public EFileVerifyLevel FileVerifyLevel { get; }
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary> /// <summary>
/// RawBundle 解密器 /// RawBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor RawBundleDecryptor { get; set; } public IBundleDecryptor RawBundleDecryptor { get; }
/// <summary> /// <summary>
/// AssetBundle 备用解密器 /// AssetBundle 备用解密器
/// </summary> /// </summary>
public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; set; } public IBundleMemoryDecryptor AssetBundleFallbackDecryptor { get; }
public Configuration(int fileVerifyMaxConcurrency, EFileVerifyLevel fileVerifyLevel,
IBundleDecryptor assetBundleDecryptor, IBundleDecryptor rawBundleDecryptor, IBundleMemoryDecryptor assetBundleFallbackDecryptor)
{
FileVerifyMaxConcurrency = fileVerifyMaxConcurrency;
FileVerifyLevel = fileVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
RawBundleDecryptor = rawBundleDecryptor;
AssetBundleFallbackDecryptor = assetBundleFallbackDecryptor;
}
} }
private const int HashFolderNameLength = 2; private const int HashFolderNameLength = 2;
@@ -61,7 +71,7 @@ namespace YooAsset
public string RootPath { get; } public string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
public bool IsReadOnly { get; } public bool IsReadOnly { get; }
@@ -101,19 +111,19 @@ namespace YooAsset
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCInitializeOperation InitializeAsync() public BCInitializeOperation InitializeAsync()
{ {
var operation = new SBCInitializeOperation(this); var operation = new SBCInitializeOperation(this);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options) public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{ {
var operation = new SBCWriteCacheOperation(this, options); var operation = new SBCWriteCacheOperation(this, options);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options) public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{ {
ICacheEvictionPolicy policy = CreateEvictionPolicy(options); ICacheEvictionPolicy policy = CreateEvictionPolicy(options);
if (policy == null) if (policy == null)
@@ -125,7 +135,9 @@ namespace YooAsset
/// <summary> /// <summary>
/// 根据 ClearMethod 创建对应的淘汰策略实例 /// 根据 ClearMethod 创建对应的淘汰策略实例
/// </summary> /// </summary>
protected virtual ICacheEvictionPolicy CreateEvictionPolicy(BCClearCacheOptions options) /// <param name="options">清理缓存选项</param>
/// <returns>与清理方式匹配的淘汰策略实例</returns>
protected ICacheEvictionPolicy CreateEvictionPolicy(BCClearCacheOptions options)
{ {
if (options.ClearMethod == ClearCacheMethods.ClearAllBundleFiles) if (options.ClearMethod == ClearCacheMethods.ClearAllBundleFiles)
return new EvictionAllPolicy(); return new EvictionAllPolicy();
@@ -142,41 +154,43 @@ namespace YooAsset
return null; return null;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options) public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{ {
var operation = new SBCVerifyCacheOperation(this, options); var operation = new SBCVerifyCacheOperation(this, options);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options) public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{ {
if (options.Bundle.BundleType == (int)EBundleType.AssetBundle) if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
{ {
var operation = new SBCLoadAssetBundleOperation(this, options.Bundle); var operation = new SBCLoadAssetBundleOperation(this, options.Bundle);
return operation; return operation;
} }
else if (options.Bundle.BundleType == (int)EBundleType.RawBundle) else if (options.Bundle.GetBundleType() == (int)EBundleType.RawBundle)
{ {
var operation = new SBCLoadRawBundleOperation(this, options.Bundle); var operation = new SBCLoadRawBundleOperation(this, options.Bundle);
return operation; return operation;
} }
else else
{ {
string error = $"{nameof(SandboxBundleCache)} does not support bundle type: {options.Bundle.BundleType}."; string error = $"{nameof(SandboxBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error); var operation = new BCLoadBundleErrorOperation(error);
return operation; return operation;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsCached(string bundleGUID) public bool IsCached(string bundleGuid)
{ {
return _cacheEntries.ContainsKey(bundleGUID); return _cacheEntries.ContainsKey(bundleGuid);
} }
#region #region
/// <summary> /// <summary>
/// 获取 Bundle 数据文件路径 /// 获取 Bundle 数据文件路径
/// </summary> /// </summary>
/// <param name="bundle">资源包描述</param>
/// <returns>数据文件的完整路径</returns>
internal string GetDataFilePath(PackageBundle bundle) internal string GetDataFilePath(PackageBundle bundle)
{ {
if (_dataFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false) if (_dataFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
@@ -191,6 +205,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取 Bundle 信息文件路径 /// 获取 Bundle 信息文件路径
/// </summary> /// </summary>
/// <param name="bundle">资源包描述</param>
/// <returns>信息文件的完整路径</returns>
internal string GetInfoFilePath(PackageBundle bundle) internal string GetInfoFilePath(PackageBundle bundle)
{ {
if (_infoFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false) if (_infoFilePathMapping.TryGetValue(bundle.BundleGuid, out string filePath) == false)
@@ -205,6 +221,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取 Bundle 数据临时文件路径 /// 获取 Bundle 数据临时文件路径
/// </summary> /// </summary>
/// <param name="bundle">资源包描述</param>
/// <returns>数据临时文件的完整路径</returns>
internal string GetDataTempFilePath(PackageBundle bundle) internal string GetDataTempFilePath(PackageBundle bundle)
{ {
string folderName = GetHashFolderName(bundle.FileHash); string folderName = GetHashFolderName(bundle.FileHash);
@@ -214,6 +232,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取 Bundle 信息临时文件路径 /// 获取 Bundle 信息临时文件路径
/// </summary> /// </summary>
/// <param name="bundle">资源包描述</param>
/// <returns>信息临时文件的完整路径</returns>
internal string GetInfoTempFilePath(PackageBundle bundle) internal string GetInfoTempFilePath(PackageBundle bundle)
{ {
string folderName = GetHashFolderName(bundle.FileHash); string folderName = GetHashFolderName(bundle.FileHash);
@@ -223,9 +243,11 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取指定缓存条目 /// 获取指定缓存条目
/// </summary> /// </summary>
internal SandboxBundleCacheEntry GetEntry(string bundleGUID) /// <param name="bundleGuid">资源包 GUID</param>
/// <returns>对应的沙盒缓存条目</returns>
internal SandboxBundleCacheEntry GetEntry(string bundleGuid)
{ {
if (_cacheEntries.TryGetValue(bundleGUID, out SandboxBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundleGuid, out SandboxBundleCacheEntry entry))
return entry; return entry;
else else
return null; return null;
@@ -234,6 +256,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取所有缓存条目 /// 获取所有缓存条目
/// </summary> /// </summary>
/// <returns>当前字典中全部沙盒缓存条目的只读集合</returns>
internal IReadOnlyCollection<SandboxBundleCacheEntry> GetAllEntries() internal IReadOnlyCollection<SandboxBundleCacheEntry> GetAllEntries()
{ {
return _cacheEntries.Values; return _cacheEntries.Values;
@@ -242,26 +265,31 @@ namespace YooAsset
/// <summary> /// <summary>
/// 添加指定缓存条目 /// 添加指定缓存条目
/// </summary> /// </summary>
internal void AddEntry(string bundleGUID, SandboxBundleCacheEntry cacheEntry) /// <param name="bundleGuid">资源包 GUID</param>
/// <param name="cacheEntry">沙盒缓存条目</param>
internal void AddEntry(string bundleGuid, SandboxBundleCacheEntry cacheEntry)
{ {
if (_cacheEntries.ContainsKey(bundleGUID)) if (_cacheEntries.ContainsKey(bundleGuid))
throw new YooInternalException($"Cache entry already exists: '{bundleGUID}'."); throw new YooInternalException($"Cache entry already exists: '{bundleGuid}'.");
_cacheEntries.Add(bundleGUID, cacheEntry); _cacheEntries.Add(bundleGuid, cacheEntry);
SpaceOccupied += cacheEntry.GetFileSize(); SpaceOccupied += cacheEntry.GetFileSize();
} }
/// <summary> /// <summary>
/// 删除指定缓存条目 /// 删除指定缓存条目
/// </summary> /// </summary>
internal void RemoveEntry(string bundleGUID) /// <param name="bundleGuid">资源包 GUID</param>
internal void RemoveEntry(string bundleGuid)
{ {
if (_cacheEntries.TryGetValue(bundleGUID, out SandboxBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundleGuid, out SandboxBundleCacheEntry entry))
{ {
_cacheEntries.Remove(bundleGUID); _cacheEntries.Remove(bundleGuid);
_dataFilePathMapping.Remove(bundleGUID); _dataFilePathMapping.Remove(bundleGuid);
_infoFilePathMapping.Remove(bundleGUID); _infoFilePathMapping.Remove(bundleGuid);
SpaceOccupied -= entry.GetFileSize(); SpaceOccupied -= entry.GetFileSize();
if (SpaceOccupied < 0)
SpaceOccupied = 0;
entry.Delete(); entry.Delete();
} }
} }

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 沙盒文件缓存常量定义 /// 沙盒文件缓存常量定义
/// </summary> /// </summary>
internal class SandboxBundleCacheConsts internal static class SandboxBundleCacheConsts
{ {
/// <summary> /// <summary>
/// 数据文件名称 /// 数据文件名称

View File

@@ -13,7 +13,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 信息文件路径 /// 信息文件路径
@@ -27,14 +27,14 @@ namespace YooAsset
/// <summary> /// <summary>
/// 创建沙盒文件缓存条目 /// 创建沙盒文件缓存条目实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="infoFilePath">信息文件路径</param> /// <param name="infoFilePath">信息文件路径</param>
/// <param name="dataFilePath">数据文件路径</param> /// <param name="dataFilePath">数据文件路径</param>
public SandboxBundleCacheEntry(string bundleGUID, string infoFilePath, string dataFilePath) public SandboxBundleCacheEntry(string bundleGuid, string infoFilePath, string dataFilePath)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
InfoFilePath = infoFilePath; InfoFilePath = infoFilePath;
DataFilePath = dataFilePath; DataFilePath = dataFilePath;
} }
@@ -61,7 +61,7 @@ namespace YooAsset
} }
catch (Exception ex) catch (Exception ex)
{ {
YooLogger.Error($"Failed to delete sandbox file. Error: {ex.Message}."); YooLogger.LogError($"Failed to delete sandbox file: {ex.Message}.");
return false; return false;
} }
} }

View File

@@ -3,14 +3,14 @@ using System.IO;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 搜索的文件信息,用于缓存文件校验流程 /// 搜索的文件信息,用于缓存文件校验流程
/// </summary> /// </summary>
internal class SearchFileInfo internal class SearchFileInfo
{ {
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 缓存文件夹路径 /// 缓存文件夹路径
@@ -33,15 +33,15 @@ namespace YooAsset
public volatile int VerifyResultCode = 0; public volatile int VerifyResultCode = 0;
/// <summary> /// <summary>
/// 创建验证文件信息 /// 创建验证文件信息实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="folderPath">缓存文件夹路径</param> /// <param name="folderPath">缓存文件夹路径</param>
/// <param name="dataFilePath">数据文件路径</param> /// <param name="dataFilePath">数据文件路径</param>
/// <param name="infoFilePath">信息文件路径</param> /// <param name="infoFilePath">信息文件路径</param>
public SearchFileInfo(string bundleGUID, string folderPath, string dataFilePath, string infoFilePath) public SearchFileInfo(string bundleGuid, string folderPath, string dataFilePath, string infoFilePath)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
FolderPath = folderPath; FolderPath = folderPath;
DataFilePath = dataFilePath; DataFilePath = dataFilePath;
InfoFilePath = infoFilePath; InfoFilePath = infoFilePath;
@@ -58,7 +58,7 @@ namespace YooAsset
} }
catch (System.Exception ex) catch (System.Exception ex)
{ {
YooLogger.Warning($"Failed to delete cache bundle folder: {ex}."); YooLogger.LogWarning($"Failed to delete cache bundle folder: {ex}.");
} }
} }
} }

View File

@@ -2,40 +2,40 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 临时的文件信息,用于存储待验证的下载文件 /// 临时的文件信息,用于存储待验证的下载文件
/// </summary> /// </summary>
internal class TempFileInfo internal class TempFileInfo
{ {
/// <summary> /// <summary>
/// 临时文件路径 /// 临时文件路径
/// </summary> /// </summary>
public string FilePath { private set; get; } public string FilePath { get; private set; }
/// <summary> /// <summary>
/// 文件CRC校验值 /// 文件CRC校验值
/// </summary> /// </summary>
public uint FileCRC { private set; get; } public uint FileCrc { get; private set; }
/// <summary> /// <summary>
/// 文件大小(字节) /// 文件大小(字节)
/// </summary> /// </summary>
public long FileSize { private set; get; } public long FileSize { get; private set; }
/// <summary> /// <summary>
/// 验证结果码(原子操作对象,用于线程安全) /// 验证结果码(原子操作对象,用于线程安全)
/// </summary> /// </summary>
public volatile int VerifyResultCode = 0; public volatile int VerifyResultCode = 0;
/// <summary> /// <summary>
/// 创建临时文件信息 /// 创建临时文件信息实例
/// </summary> /// </summary>
/// <param name="filePath">临时文件路径</param> /// <param name="filePath">临时文件路径</param>
/// <param name="fileCRC">文件CRC校验值</param> /// <param name="fileCrc">文件CRC校验值</param>
/// <param name="fileSize">文件大小(字节)</param> /// <param name="fileSize">文件大小(字节)</param>
public TempFileInfo(string filePath, uint fileCRC, long fileSize) public TempFileInfo(string filePath, uint fileCrc, long fileSize)
{ {
FilePath = filePath; FilePath = filePath;
FileCRC = fileCRC; FileCrc = fileCrc;
FileSize = fileSize; FileSize = fileSize;
} }
} }

View File

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

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// Web远端文件缓存加载 AssetBundle 操作 /// Web远端文件缓存加载 AssetBundle 操作
/// </summary> /// </summary>
internal class WRBCLoadAssetBundleOperation : BCLoadBundleOperation internal sealed class WRBCLoadAssetBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -20,6 +20,11 @@ namespace YooAsset
private WebRemoteBundleCacheEntry _cacheEntry; private WebRemoteBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建 Web 远端 AssetBundle 加载操作实例
/// </summary>
/// <param name="fileCache">Web 远端文件缓存系统</param>
/// <param name="options">加载资源包操作选项</param>
public WRBCLoadAssetBundleOperation(WebRemoteBundleCache fileCache, BCLoadBundleOptions options) public WRBCLoadAssetBundleOperation(WebRemoteBundleCache fileCache, BCLoadBundleOptions options)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -52,17 +57,17 @@ namespace YooAsset
{ {
if (_loadWebAssetBundleOp == null) if (_loadWebAssetBundleOp == null)
{ {
var options = new LoadWebAssetBundleOptions(); var options = new LoadWebAssetBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _options.Bundle; bundle: _options.Bundle,
options.CandidateUrls = _cacheEntry.URLs; candidateUrls: _cacheEntry.URLs,
options.AssetBundleDecryptor = _fileCache.Config.AssetBundleDecryptor; assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
options.DownloadBackend = _fileCache.Config.DownloadBackend; downloadBackend: _fileCache.Config.DownloadBackend,
options.DownloadVerifyLevel = _fileCache.Config.DownloadVerifyLevel; downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
options.WatchdogTimeout = _fileCache.Config.WatchdogTimeout; watchdogTimeout: _fileCache.Config.WatchdogTimeout,
options.DisableUnityWebCache = _fileCache.Config.DisableUnityWebCache; disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
options.DownloadRetryPolicy = _fileCache.Config.DownloadRetryPolicy; downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
options.DownloadUrlPolicy = _fileCache.Config.DownloadUrlPolicy; downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
if (_options.Bundle.IsEncrypted) if (_options.Bundle.IsEncrypted)
_loadWebAssetBundleOp = new LoadWebEncryptedAssetBundleOperation(options); _loadWebAssetBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
@@ -99,7 +104,7 @@ namespace YooAsset
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"{nameof(WebRemoteBundleCache)} does not support synchronous asset bundle loading."); SetError($"{nameof(WebRemoteBundleCache)} does not support synchronous asset bundle loading.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
} }
} }

View File

@@ -4,54 +4,68 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// Web远端文件缓存系统用于从远程服务器加载资源 /// Web远端文件缓存系统用于从远程服务器加载资源
/// </summary> /// </summary>
internal class WebRemoteBundleCache : IBundleCache internal class WebRemoteBundleCache : IBundleCache
{ {
/// <summary> /// <summary>
/// Web远端文件缓存配置 /// Web远端文件缓存配置
/// </summary> /// </summary>
internal struct Configuration internal readonly struct Configuration
{ {
/// <summary> /// <summary>
/// 看门狗超时时间 /// 看门狗超时时间
/// </summary> /// </summary>
public int WatchdogTimeout { get; set; } public int WatchdogTimeout { get; }
/// <summary> /// <summary>
/// 禁用Unity的网络缓存 /// 禁用Unity的网络缓存
/// </summary> /// </summary>
public bool DisableUnityWebCache { get; set; } public bool DisableUnityWebCache { get; }
/// <summary> /// <summary>
/// 下载数据校验级别 /// 下载数据校验级别
/// </summary> /// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; set; } public EFileVerifyLevel DownloadVerifyLevel { get; }
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary> /// <summary>
/// 远程服务接口 /// 远程服务接口
/// </summary> /// </summary>
public IRemoteService RemoteService { get; set; } public IRemoteService RemoteService { get; }
/// <summary> /// <summary>
/// 下载后台接口 /// 下载后台接口
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; set; } public IDownloadBackend DownloadBackend { get; }
/// <summary> /// <summary>
/// 下载重试判定策略 /// 下载重试判定策略
/// </summary> /// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; set; } public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary> /// <summary>
/// URL 选择策略 /// URL 选择策略
/// </summary> /// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; set; } public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor, IRemoteService remoteService,
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadVerifyLevel = downloadVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
RemoteService = remoteService;
DownloadBackend = downloadBackend;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
} }
private readonly Dictionary<string, WebRemoteBundleCacheEntry> _cacheEntries = new Dictionary<string, WebRemoteBundleCacheEntry>(10000); private readonly Dictionary<string, WebRemoteBundleCacheEntry> _cacheEntries = new Dictionary<string, WebRemoteBundleCacheEntry>(10000);
@@ -73,7 +87,7 @@ namespace YooAsset
public string RootPath { get; } public string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
public bool IsReadOnly { get; } public bool IsReadOnly { get; }
@@ -113,46 +127,46 @@ namespace YooAsset
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCInitializeOperation InitializeAsync() public BCInitializeOperation InitializeAsync()
{ {
var operation = new WRBCInitializeOperation(this); var operation = new WRBCInitializeOperation(this);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options) public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{ {
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebRemoteBundleCache)} is readonly."); var operation = new BCWriteCacheCompleteOperation($"{nameof(WebRemoteBundleCache)} is readonly.");
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options) public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{ {
var operation = new BCClearCacheCompleteOperation(); var operation = new BCClearCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options) public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{ {
var operation = new BCVerifyCacheCompleteOperation(); var operation = new BCVerifyCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options) public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{ {
if (options.Bundle.BundleType == (int)EBundleType.AssetBundle) if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
{ {
var operation = new WRBCLoadAssetBundleOperation(this, options); var operation = new WRBCLoadAssetBundleOperation(this, options);
return operation; return operation;
} }
else else
{ {
string error = $"{nameof(WebRemoteBundleCache)} does not support bundle type: {options.Bundle.BundleType}."; string error = $"{nameof(WebRemoteBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error); var operation = new BCLoadBundleErrorOperation(error);
return operation; return operation;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsCached(string bundleGUID) public bool IsCached(string bundleGuid)
{ {
return true; return true;
} }
@@ -161,6 +175,8 @@ namespace YooAsset
/// <summary> /// <summary>
/// 获取或创建指定资源包的缓存条目 /// 获取或创建指定资源包的缓存条目
/// </summary> /// </summary>
/// <param name="bundle">资源包描述</param>
/// <returns>已存在则返回对应条目;否则创建并登记后返回新条目。</returns>
internal WebRemoteBundleCacheEntry GetEntry(PackageBundle bundle) internal WebRemoteBundleCacheEntry GetEntry(PackageBundle bundle)
{ {
if (_cacheEntries.TryGetValue(bundle.BundleGuid, out WebRemoteBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundle.BundleGuid, out WebRemoteBundleCacheEntry entry))
@@ -169,7 +185,7 @@ namespace YooAsset
} }
else else
{ {
var urls = Config.RemoteService.GetRemoteUrls(bundle.FileName); var urls = Config.RemoteService.GetRemoteUrls(bundle.GetFileName());
var newEntry = new WebRemoteBundleCacheEntry(bundle.BundleGuid, urls); var newEntry = new WebRemoteBundleCacheEntry(bundle.BundleGuid, urls);
_cacheEntries.Add(bundle.BundleGuid, newEntry); _cacheEntries.Add(bundle.BundleGuid, newEntry);
return newEntry; return newEntry;

View File

@@ -10,7 +10,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 候选下载地址列表 /// 候选下载地址列表
@@ -18,13 +18,13 @@ namespace YooAsset
public IReadOnlyList<string> URLs { get; private set; } public IReadOnlyList<string> URLs { get; private set; }
/// <summary> /// <summary>
/// 创建Web远端文件缓存条目 /// 创建 Web 远端文件缓存条目实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="urls">候选下载地址列表</param> /// <param name="urls">候选下载地址列表</param>
public WebRemoteBundleCacheEntry(string bundleGUID, IReadOnlyList<string> urls) public WebRemoteBundleCacheEntry(string bundleGuid, IReadOnlyList<string> urls)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
URLs = urls; URLs = urls;
} }
} }

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// Web服务器文件缓存初始化操作 /// Web服务器文件缓存初始化操作
/// </summary> /// </summary>
internal class WSBCInitializeOperation : BCInitializeOperation internal sealed class WSBCInitializeOperation : BCInitializeOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -18,9 +18,13 @@ namespace YooAsset
private LoadBuiltinCatalogOperation _loadBuiltinCatalogOp; private LoadBuiltinCatalogOperation _loadBuiltinCatalogOp;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public WSBCInitializeOperation(WebServerBundleCache cache) /// <summary>
/// 创建 Web 服务器缓存初始化操作实例
/// </summary>
/// <param name="fileCache">Web 服务器文件缓存系统</param>
public WSBCInitializeOperation(WebServerBundleCache fileCache)
{ {
_fileCache = cache; _fileCache = fileCache;
} }
protected override void InternalStart() protected override void InternalStart()
{ {
@@ -35,10 +39,10 @@ namespace YooAsset
{ {
if (_loadBuiltinCatalogOp == null) if (_loadBuiltinCatalogOp == null)
{ {
var options = new LoadBuiltinCatalogOptions(); var options = new LoadBuiltinCatalogOptions(
options.PackageName = _fileCache.PackageName; packageName: _fileCache.PackageName,
options.FilePath = _fileCache.GetCatalogBinaryFileLoadPath(); filePath: _fileCache.GetCatalogBinaryFileLoadPath(),
options.DownloadBackend = _fileCache.Config.DownloadBackend; downloadBackend: _fileCache.Config.DownloadBackend);
_loadBuiltinCatalogOp = new LoadBuiltinCatalogOperation(options); _loadBuiltinCatalogOp = new LoadBuiltinCatalogOperation(options);
_loadBuiltinCatalogOp.StartOperation(); _loadBuiltinCatalogOp.StartOperation();
AddChildOperation(_loadBuiltinCatalogOp); AddChildOperation(_loadBuiltinCatalogOp);
@@ -65,8 +69,8 @@ namespace YooAsset
foreach (var fileEntry in catalog.Entries) foreach (var fileEntry in catalog.Entries)
{ {
string filePath = PathUtility.Combine(_fileCache.RootPath, fileEntry.FileName); string filePath = PathUtility.Combine(_fileCache.RootPath, fileEntry.FileName);
var cacheEntry = new WebServerBundleCacheEntry(fileEntry.BundleGUID, filePath); var cacheEntry = new WebServerBundleCacheEntry(fileEntry.BundleGuid, filePath);
_fileCache.AddEntry(fileEntry.BundleGUID, cacheEntry); _fileCache.AddEntry(fileEntry.BundleGuid, cacheEntry);
} }
_steps = ESteps.Done; _steps = ESteps.Done;

View File

@@ -4,7 +4,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// Web服务器文件缓存加载 AssetBundle 操作 /// Web服务器文件缓存加载 AssetBundle 操作
/// </summary> /// </summary>
internal class WSBCLoadAssetBundleOperation : BCLoadBundleOperation internal sealed class WSBCLoadAssetBundleOperation : BCLoadBundleOperation
{ {
private enum ESteps private enum ESteps
{ {
@@ -20,6 +20,11 @@ namespace YooAsset
private WebServerBundleCacheEntry _cacheEntry; private WebServerBundleCacheEntry _cacheEntry;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建 Web 服务器 AssetBundle 加载操作实例
/// </summary>
/// <param name="fileCache">Web 服务器文件缓存系统</param>
/// <param name="options">加载资源包操作选项</param>
public WSBCLoadAssetBundleOperation(WebServerBundleCache fileCache, BCLoadBundleOptions options) public WSBCLoadAssetBundleOperation(WebServerBundleCache fileCache, BCLoadBundleOptions options)
{ {
_fileCache = fileCache; _fileCache = fileCache;
@@ -53,17 +58,17 @@ namespace YooAsset
if (_loadWebAssetBundleOp == null) if (_loadWebAssetBundleOp == null)
{ {
string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath); string url = DownloadUrlHelper.ToLocalFileUrl(_cacheEntry.FilePath);
var options = new LoadWebAssetBundleOptions(); var options = new LoadWebAssetBundleOptions(
options.CacheName = _fileCache.GetType().Name; cacheName: _fileCache.GetType().Name,
options.Bundle = _options.Bundle; bundle: _options.Bundle,
options.CandidateUrls = new[] { url }; candidateUrls: new[] { url },
options.AssetBundleDecryptor = _fileCache.Config.AssetBundleDecryptor; assetBundleDecryptor: _fileCache.Config.AssetBundleDecryptor,
options.DownloadBackend = _fileCache.Config.DownloadBackend; downloadBackend: _fileCache.Config.DownloadBackend,
options.DownloadVerifyLevel = _fileCache.Config.DownloadVerifyLevel; downloadVerifyLevel: _fileCache.Config.DownloadVerifyLevel,
options.WatchdogTimeout = _fileCache.Config.WatchdogTimeout; watchdogTimeout: _fileCache.Config.WatchdogTimeout,
options.DisableUnityWebCache = _fileCache.Config.DisableUnityWebCache; disableUnityWebCache: _fileCache.Config.DisableUnityWebCache,
options.DownloadRetryPolicy = _fileCache.Config.DownloadRetryPolicy; downloadRetryPolicy: _fileCache.Config.DownloadRetryPolicy,
options.DownloadUrlPolicy = _fileCache.Config.DownloadUrlPolicy; downloadUrlPolicy: _fileCache.Config.DownloadUrlPolicy);
if (_options.Bundle.IsEncrypted) if (_options.Bundle.IsEncrypted)
_loadWebAssetBundleOp = new LoadWebEncryptedAssetBundleOperation(options); _loadWebAssetBundleOp = new LoadWebEncryptedAssetBundleOperation(options);
@@ -100,7 +105,7 @@ namespace YooAsset
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"{nameof(WebServerBundleCache)} does not support synchronous asset bundle loading."); SetError($"{nameof(WebServerBundleCache)} does not support synchronous asset bundle loading.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
} }
} }

View File

@@ -4,49 +4,62 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// Web服务器文件缓存系统用于WebGL平台从服务器加载资源 /// Web服务器文件缓存系统用于WebGL平台从服务器加载资源
/// </summary> /// </summary>
internal class WebServerBundleCache : IBundleCache internal class WebServerBundleCache : IBundleCache
{ {
/// <summary> /// <summary>
/// Web服务器文件缓存配置 /// Web服务器文件缓存配置
/// </summary> /// </summary>
internal struct Configuration internal readonly struct Configuration
{ {
/// <summary> /// <summary>
/// 看门狗超时时间 /// 看门狗超时时间
/// </summary> /// </summary>
public int WatchdogTimeout { get; set; } public int WatchdogTimeout { get; }
/// <summary> /// <summary>
/// 禁用Unity的网络缓存 /// 禁用Unity的网络缓存
/// </summary> /// </summary>
public bool DisableUnityWebCache { get; set; } public bool DisableUnityWebCache { get; }
/// <summary> /// <summary>
/// 下载数据校验级别 /// 下载数据校验级别
/// </summary> /// </summary>
public EFileVerifyLevel DownloadVerifyLevel { get; set; } public EFileVerifyLevel DownloadVerifyLevel { get; }
/// <summary> /// <summary>
/// AssetBundle 解密器 /// AssetBundle 解密器
/// </summary> /// </summary>
public IBundleDecryptor AssetBundleDecryptor { get; set; } public IBundleDecryptor AssetBundleDecryptor { get; }
/// <summary> /// <summary>
/// 下载后台 /// 下载后台
/// </summary> /// </summary>
public IDownloadBackend DownloadBackend { get; set; } public IDownloadBackend DownloadBackend { get; }
/// <summary> /// <summary>
/// 下载重试判定策略 /// 下载重试判定策略
/// </summary> /// </summary>
public IDownloadRetryPolicy DownloadRetryPolicy { get; set; } public IDownloadRetryPolicy DownloadRetryPolicy { get; }
/// <summary> /// <summary>
/// URL 选择策略 /// URL 选择策略
/// </summary> /// </summary>
public IDownloadUrlPolicy DownloadUrlPolicy { get; set; } public IDownloadUrlPolicy DownloadUrlPolicy { get; }
public Configuration(int watchdogTimeout, bool disableUnityWebCache,
EFileVerifyLevel downloadVerifyLevel, IBundleDecryptor assetBundleDecryptor,
IDownloadBackend downloadBackend, IDownloadRetryPolicy downloadRetryPolicy, IDownloadUrlPolicy downloadUrlPolicy)
{
WatchdogTimeout = watchdogTimeout;
DisableUnityWebCache = disableUnityWebCache;
DownloadVerifyLevel = downloadVerifyLevel;
AssetBundleDecryptor = assetBundleDecryptor;
DownloadBackend = downloadBackend;
DownloadRetryPolicy = downloadRetryPolicy;
DownloadUrlPolicy = downloadUrlPolicy;
}
} }
private readonly Dictionary<string, WebServerBundleCacheEntry> _cacheEntries = new Dictionary<string, WebServerBundleCacheEntry>(10000); private readonly Dictionary<string, WebServerBundleCacheEntry> _cacheEntries = new Dictionary<string, WebServerBundleCacheEntry>(10000);
@@ -68,7 +81,7 @@ namespace YooAsset
public string RootPath { get; } public string RootPath { get; }
/// <summary> /// <summary>
/// 只读属性 /// 是否为只读缓存
/// </summary> /// </summary>
public bool IsReadOnly { get; } public bool IsReadOnly { get; }
@@ -108,57 +121,59 @@ namespace YooAsset
{ {
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCInitializeOperation InitializeAsync() public BCInitializeOperation InitializeAsync()
{ {
var operation = new WSBCInitializeOperation(this); var operation = new WSBCInitializeOperation(this);
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options) public BCWriteCacheOperation WriteCacheAsync(BCWriteCacheOptions options)
{ {
var operation = new BCWriteCacheCompleteOperation($"{nameof(WebServerBundleCache)} is readonly."); var operation = new BCWriteCacheCompleteOperation($"{nameof(WebServerBundleCache)} is readonly.");
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options) public BCClearCacheOperation ClearCacheAsync(BCClearCacheOptions options)
{ {
var operation = new BCClearCacheCompleteOperation(); var operation = new BCClearCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options) public BCVerifyCacheOperation VerifyCacheAsync(BCVerifyCacheOptions options)
{ {
var operation = new BCVerifyCacheCompleteOperation(); var operation = new BCVerifyCacheCompleteOperation();
return operation; return operation;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options) public BCLoadBundleOperation LoadBundleAsync(BCLoadBundleOptions options)
{ {
if (options.Bundle.BundleType == (int)EBundleType.AssetBundle) if (options.Bundle.GetBundleType() == (int)EBundleType.AssetBundle)
{ {
var operation = new WSBCLoadAssetBundleOperation(this, options); var operation = new WSBCLoadAssetBundleOperation(this, options);
return operation; return operation;
} }
else else
{ {
string error = $"{nameof(WebServerBundleCache)} does not support bundle type: {options.Bundle.BundleType}."; string error = $"{nameof(WebServerBundleCache)} does not support bundle type: {options.Bundle.GetBundleType()}.";
var operation = new BCLoadBundleErrorOperation(error); var operation = new BCLoadBundleErrorOperation(error);
return operation; return operation;
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual bool IsCached(string bundleGUID) public bool IsCached(string bundleGuid)
{ {
return _cacheEntries.ContainsKey(bundleGUID); return _cacheEntries.ContainsKey(bundleGuid);
} }
#region #region
/// <summary> /// <summary>
/// 获取指定缓存条目 /// 获取指定缓存条目
/// </summary> /// </summary>
internal WebServerBundleCacheEntry GetEntry(string bundleGUID) /// <param name="bundleGuid">资源包 GUID</param>
/// <returns>对应的 Web 服务器缓存条目</returns>
internal WebServerBundleCacheEntry GetEntry(string bundleGuid)
{ {
if (_cacheEntries.TryGetValue(bundleGUID, out WebServerBundleCacheEntry entry)) if (_cacheEntries.TryGetValue(bundleGuid, out WebServerBundleCacheEntry entry))
return entry; return entry;
else else
return null; return null;
@@ -167,17 +182,20 @@ namespace YooAsset
/// <summary> /// <summary>
/// 添加指定缓存条目 /// 添加指定缓存条目
/// </summary> /// </summary>
internal void AddEntry(string bundleGUID, WebServerBundleCacheEntry cacheEntry) /// <param name="bundleGuid">资源包 GUID</param>
/// <param name="cacheEntry">Web 服务器缓存条目</param>
internal void AddEntry(string bundleGuid, WebServerBundleCacheEntry cacheEntry)
{ {
if (_cacheEntries.ContainsKey(bundleGUID)) if (_cacheEntries.ContainsKey(bundleGuid))
throw new YooInternalException($"Cache entry already exists: '{bundleGUID}'."); throw new YooInternalException($"Cache entry already exists: '{bundleGuid}'.");
_cacheEntries.Add(bundleGUID, cacheEntry); _cacheEntries.Add(bundleGuid, cacheEntry);
} }
/// <summary> /// <summary>
/// 获取Catalog文件加载路径 /// 获取Catalog文件加载路径
/// </summary> /// </summary>
/// <returns>文件的完整加载路径</returns>
internal string GetCatalogBinaryFileLoadPath() internal string GetCatalogBinaryFileLoadPath()
{ {
return PathUtility.Combine(RootPath, BuiltinCatalogConsts.BinaryFileName); return PathUtility.Combine(RootPath, BuiltinCatalogConsts.BinaryFileName);

View File

@@ -9,7 +9,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 资源包唯一标识 /// 资源包唯一标识
/// </summary> /// </summary>
public string BundleGUID { get; private set; } public string BundleGuid { get; private set; }
/// <summary> /// <summary>
/// 资源包文件路径 /// 资源包文件路径
@@ -17,13 +17,13 @@ namespace YooAsset
public string FilePath { get; private set; } public string FilePath { get; private set; }
/// <summary> /// <summary>
/// 创建Web服务器文件缓存条目 /// 创建 Web 服务器文件缓存条目实例
/// </summary> /// </summary>
/// <param name="bundleGUID">资源包唯一标识</param> /// <param name="bundleGuid">资源包唯一标识</param>
/// <param name="filePath">资源包文件路径</param> /// <param name="filePath">资源包文件路径</param>
public WebServerBundleCacheEntry(string bundleGUID, string filePath) public WebServerBundleCacheEntry(string bundleGuid, string filePath)
{ {
BundleGUID = bundleGUID; BundleGuid = bundleGuid;
FilePath = filePath; FilePath = filePath;
} }
} }

View File

@@ -2,14 +2,14 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 资源包类型枚举 /// 资源包类型
/// </summary> /// </summary>
public enum EBundleType public enum EBundleType
{ {
/// <summary> /// <summary>
/// 未类型 /// 未指定类型
/// </summary> /// </summary>
Unknown = 0, None = 0,
/// <summary> /// <summary>
/// 虚拟资源包 /// 虚拟资源包

View File

@@ -3,7 +3,7 @@ using UnityEngine.SceneManagement;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 资源包句柄接口,提供对已加载资源包的操作能力 /// 资源包句柄接口,提供对已加载资源包的操作能力
/// </summary> /// </summary>
internal interface IBundleHandle internal interface IBundleHandle
{ {
@@ -18,31 +18,31 @@ namespace YooAsset
void UnloadBundle(); void UnloadBundle();
/// <summary> /// <summary>
/// 异步加载指定的主资源对象 /// 加载指定的主资源对象
/// </summary> /// </summary>
/// <param name="assetInfo">待加载资源的描述信息</param> /// <param name="assetInfo">待加载资源的描述信息</param>
/// <returns>用于跟踪加载过程的异步操作对象</returns> /// <returns>用于跟踪加载过程的异步操作对象</returns>
BHLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo); BHLoadAssetOperation LoadAssetAsync(AssetInfo assetInfo);
/// <summary> /// <summary>
/// 异步加载资源包内的全部资源对象 /// 加载资源包内的全部资源对象
/// </summary> /// </summary>
/// <param name="assetInfo">待加载资源的描述信息</param> /// <param name="assetInfo">待加载资源的描述信息</param>
/// <returns>用于跟踪加载过程的异步操作对象</returns> /// <returns>用于跟踪加载过程的异步操作对象</returns>
BHLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo); BHLoadAllAssetsOperation LoadAllAssetsAsync(AssetInfo assetInfo);
/// <summary> /// <summary>
/// 异步加载指定资源对应的全部子资源对象 /// 加载指定资源对应的全部子资源对象
/// </summary> /// </summary>
/// <param name="assetInfo">待加载资源的描述信息</param> /// <param name="assetInfo">待加载资源的描述信息</param>
/// <returns>用于跟踪加载过程的异步操作对象</returns> /// <returns>用于跟踪加载过程的异步操作对象</returns>
BHLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo); BHLoadSubAssetsOperation LoadSubAssetsAsync(AssetInfo assetInfo);
/// <summary> /// <summary>
/// 异步加载指定的场景资源 /// 加载指定的场景资源
/// </summary> /// </summary>
/// <param name="assetInfo">待加载场景的资源信息</param> /// <param name="assetInfo">待加载场景的资源信息</param>
/// <param name="loadSceneParams">场景加载参数</param> /// <param name="loadSceneParams">场景加载选项</param>
/// <param name="allowSceneActivation">是否允许场景在加载完成后立即激活</param> /// <param name="allowSceneActivation">是否允许场景在加载完成后立即激活</param>
/// <returns>用于跟踪场景加载过程的异步操作对象</returns> /// <returns>用于跟踪场景加载过程的异步操作对象</returns>
BHLoadSceneOperation LoadSceneAsync(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation); BHLoadSceneOperation LoadSceneAsync(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation);

View File

@@ -14,6 +14,14 @@ namespace YooAsset
/// <summary> /// <summary>
/// 允许场景在加载完成后进入激活阶段 /// 允许场景在加载完成后进入激活阶段
/// </summary> /// </summary>
public abstract void AllowSceneActivation(); public void AllowSceneActivation()
{
InternalAllowSceneActivation();
}
/// <summary>
/// 执行场景激活的方法
/// </summary>
protected abstract void InternalAllowSceneActivation();
} }
} }

View File

@@ -7,7 +7,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// AssetBundle资源包句柄 /// AssetBundle资源包句柄
/// </summary> /// </summary>
internal class AssetBundleHandle : IBundleHandle internal sealed class AssetBundleHandle : IBundleHandle
{ {
private readonly string _bundleFilePath; private readonly string _bundleFilePath;
private readonly PackageBundle _packageBundle; private readonly PackageBundle _packageBundle;
@@ -22,6 +22,13 @@ namespace YooAsset
get { return _bundleFilePath; } get { return _bundleFilePath; }
} }
/// <summary>
/// 创建AssetBundle资源包句柄实例
/// </summary>
/// <param name="bundleFilePath">资源包文件的本地路径</param>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetBundle">已加载的 Unity AssetBundle 对象</param>
/// <param name="bundleStream">加载 AssetBundle 时使用的文件流</param>
public AssetBundleHandle(string bundleFilePath, PackageBundle packageBundle, AssetBundle assetBundle, Stream bundleStream) public AssetBundleHandle(string bundleFilePath, PackageBundle packageBundle, AssetBundle assetBundle, Stream bundleStream)
{ {
_bundleFilePath = bundleFilePath; _bundleFilePath = bundleFilePath;
@@ -42,7 +49,6 @@ namespace YooAsset
if (_bundleStream != null) if (_bundleStream != null)
{ {
_bundleStream.Close();
_bundleStream.Dispose(); _bundleStream.Dispose();
} }
} }

View File

@@ -3,11 +3,11 @@ using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// AssetBundle的加载所有资源操作 /// AssetBundle 中加载全部资源对象
/// </summary> /// </summary>
internal class ABHLoadAllAssetsOperation : BHLoadAllAssetsOperation internal sealed class ABHLoadAllAssetsOperation : BHLoadAllAssetsOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -22,6 +22,12 @@ namespace YooAsset
private AssetBundleRequest _request; private AssetBundleRequest _request;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetBundle">已加载的 Unity AssetBundle 对象</param>
/// <param name="assetInfo">待加载资源信息</param>
public ABHLoadAllAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo) public ABHLoadAllAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -42,7 +48,7 @@ namespace YooAsset
if (_assetBundle == null) if (_assetBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"The bundle {_packageBundle.BundleName} has been destroyed due to Unity engine bugs."); SetError($"Bundle '{_packageBundle.BundleName}' has been destroyed due to Unity engine bugs.");
return; return;
} }
@@ -77,7 +83,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 强制挂起主线程(注意:该操作会很耗时) // 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Blocking the main thread while loading Unity assets."); YooLogger.LogWarning("Blocking the main thread while loading Unity assets.");
Result = _request.allAssets; Result = _request.allAssets;
} }
else else
@@ -93,13 +99,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load all assets: {_assetInfo.AssetPath} AssetType: null AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load all assets: '{_assetInfo.AssetPath}' AssetType: null AssetBundle: '{_packageBundle.BundleName}'.";
else else
error = $"Failed to load all assets: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType} AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load all assets: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType} AssetBundle: '{_packageBundle.BundleName}'.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(error); YooLogger.LogError(error);
} }
else else
{ {

View File

@@ -3,11 +3,11 @@ using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// AssetBundle加载单个资源操作 /// AssetBundle加载单个资源对象
/// </summary> /// </summary>
internal class ABHLoadAssetOperation : BHLoadAssetOperation internal sealed class ABHLoadAssetOperation : BHLoadAssetOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -22,6 +22,12 @@ namespace YooAsset
private AssetBundleRequest _request; private AssetBundleRequest _request;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetBundle">已加载的 Unity AssetBundle 对象</param>
/// <param name="assetInfo">待加载资源信息</param>
public ABHLoadAssetOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo) public ABHLoadAssetOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -42,7 +48,7 @@ namespace YooAsset
if (_assetBundle == null) if (_assetBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"The bundle {_packageBundle.BundleName} has been destroyed due to Unity engine bugs."); SetError($"Bundle '{_packageBundle.BundleName}' has been destroyed due to Unity engine bugs.");
return; return;
} }
@@ -77,7 +83,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 强制挂起主线程(注意:该操作会很耗时) // 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Blocking the main thread while loading a Unity asset."); YooLogger.LogWarning("Blocking the main thread while loading a Unity asset.");
Result = _request.asset; Result = _request.asset;
} }
else else
@@ -93,13 +99,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load asset: {_assetInfo.AssetPath} AssetType: null AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: null AssetBundle: '{_packageBundle.BundleName}'.";
else else
error = $"Failed to load asset: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType} AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType} AssetBundle: '{_packageBundle.BundleName}'.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
else else
{ {

View File

@@ -4,11 +4,11 @@ using UnityEngine.SceneManagement;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// AssetBundle的场景加载操作 /// AssetBundle 中加载场景资源
/// </summary> /// </summary>
internal class ABHLoadSceneOperation : BHLoadSceneOperation internal sealed class ABHLoadSceneOperation : BHLoadSceneOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
LoadScene, LoadScene,
@@ -22,6 +22,12 @@ namespace YooAsset
private AsyncOperation _asyncOperation; private AsyncOperation _asyncOperation;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="assetInfo">待加载场景信息</param>
/// <param name="loadSceneParams">控制场景加载的选项</param>
/// <param name="allowSceneActivation">是否允许场景在加载完成后立即激活</param>
public ABHLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation) public ABHLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation)
{ {
_assetInfo = assetInfo; _assetInfo = assetInfo;
@@ -60,8 +66,8 @@ namespace YooAsset
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Failed to load scene: {_assetInfo.AssetPath}"); SetError($"Failed to load scene: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
} }
} }
@@ -73,7 +79,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 注意:场景加载无法强制异步转同步 // 注意:场景加载无法强制异步转同步
YooLogger.Error("The scene is already loading asynchronously."); YooLogger.LogError("The scene is already loading asynchronously.");
} }
else else
{ {
@@ -98,8 +104,8 @@ namespace YooAsset
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"The loaded scene is invalid: {_assetInfo.AssetPath}"); SetError($"Loaded scene is invalid: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
} }
} }
@@ -108,7 +114,7 @@ namespace YooAsset
//注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法! //注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法!
ExecuteOnce(); ExecuteOnce();
} }
public override void AllowSceneActivation() protected override void InternalAllowSceneActivation()
{ {
_allowSceneActivation = true; _allowSceneActivation = true;
} }

View File

@@ -3,11 +3,11 @@ using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// AssetBundle的加载子资源操作 /// AssetBundle 中加载指定资源的全部子资源对象
/// </summary> /// </summary>
internal class ABHLoadSubAssetsOperation : BHLoadSubAssetsOperation internal sealed class ABHLoadSubAssetsOperation : BHLoadSubAssetsOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -22,6 +22,12 @@ namespace YooAsset
private AssetBundleRequest _request; private AssetBundleRequest _request;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetBundle">已加载的 Unity AssetBundle 对象</param>
/// <param name="assetInfo">待加载资源信息</param>
public ABHLoadSubAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo) public ABHLoadSubAssetsOperation(PackageBundle packageBundle, AssetBundle assetBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -42,7 +48,7 @@ namespace YooAsset
if (_assetBundle == null) if (_assetBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"The bundle {_packageBundle.BundleName} has been destroyed due to Unity engine bugs."); SetError($"Bundle '{_packageBundle.BundleName}' has been destroyed due to Unity engine bugs.");
return; return;
} }
@@ -77,7 +83,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 强制挂起主线程(注意:该操作会很耗时) // 强制挂起主线程(注意:该操作会很耗时)
YooLogger.Warning("Blocking the main thread while loading Unity assets."); YooLogger.LogWarning("Blocking the main thread while loading Unity assets.");
Result = _request.allAssets; Result = _request.allAssets;
} }
else else
@@ -93,13 +99,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load sub-assets: {_assetInfo.AssetPath} AssetType: null AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load sub-assets: '{_assetInfo.AssetPath}' AssetType: null AssetBundle: '{_packageBundle.BundleName}'.";
else else
error = $"Failed to load sub-assets: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType} AssetBundle: {_packageBundle.BundleName}"; error = $"Failed to load sub-assets: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType} AssetBundle: '{_packageBundle.BundleName}'.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(error); YooLogger.LogError(error);
} }
else else
{ {

View File

@@ -2,9 +2,9 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 原生资源包的加载所有资源操作(不支持) /// 全部资源加载操作(原生资源包不支持)
/// </summary> /// </summary>
internal class RBHLoadAllAssetsOperation : BHLoadAllAssetsOperation internal sealed class RBHLoadAllAssetsOperation : BHLoadAllAssetsOperation
{ {
protected override void InternalStart() protected override void InternalStart()
{ {

View File

@@ -2,11 +2,11 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 原生资源包加载单个资源操作 /// 原生资源包加载单个资源对象
/// </summary> /// </summary>
internal class RBHLoadAssetOperation : BHLoadAssetOperation internal sealed class RBHLoadAssetOperation : BHLoadAssetOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
LoadObject, LoadObject,
@@ -19,6 +19,12 @@ namespace YooAsset
private readonly AssetInfo _assetInfo; private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="rawBundle">已加载的原生资源包数据对象</param>
/// <param name="assetInfo">待加载资源信息</param>
public RBHLoadAssetOperation(PackageBundle packageBundle, RawBundle rawBundle, AssetInfo assetInfo) public RBHLoadAssetOperation(PackageBundle packageBundle, RawBundle rawBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -45,8 +51,8 @@ namespace YooAsset
if (Result == null) if (Result == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Failed to load raw file object: {_assetInfo.AssetPath}"); SetError($"Failed to load raw file object: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
else else
{ {
@@ -55,5 +61,9 @@ namespace YooAsset
} }
} }
} }
protected override void InternalWaitForCompletion()
{
ExecuteBatch();
}
} }
} }

View File

@@ -2,9 +2,9 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 原生资源包的场景加载操作(不支持) /// 场景加载操作(原生资源包不支持)
/// </summary> /// </summary>
internal class RBHLoadSceneOperation : BHLoadSceneOperation internal sealed class RBHLoadSceneOperation : BHLoadSceneOperation
{ {
protected override void InternalStart() protected override void InternalStart()
{ {
@@ -13,7 +13,7 @@ namespace YooAsset
protected override void InternalUpdate() protected override void InternalUpdate()
{ {
} }
public override void AllowSceneActivation() protected override void InternalAllowSceneActivation()
{ {
} }
} }

View File

@@ -2,9 +2,9 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 原生资源包的加载子资源操作(不支持) /// 子资源加载操作(原生资源包不支持)
/// </summary> /// </summary>
internal class RBHLoadSubAssetsOperation : BHLoadSubAssetsOperation internal sealed class RBHLoadSubAssetsOperation : BHLoadSubAssetsOperation
{ {
protected override void InternalStart() protected override void InternalStart()
{ {

View File

@@ -1,25 +1,37 @@
using System;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 封装原生文件字节数据的资源包对象 /// 封装原生文件字节数据的资源包对象
/// </summary> /// </summary>
public class RawBundle internal class RawBundle
{ {
private byte[] _bytes; private byte[] _bytes;
private RawFileObject _cachedObject;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="bytes">原生文件的字节数据</param>
public RawBundle(byte[] bytes) public RawBundle(byte[] bytes)
{ {
if (bytes == null)
throw new ArgumentNullException(nameof(bytes));
_bytes = bytes; _bytes = bytes;
} }
/// <summary> /// <summary>
/// 根据当前字节数据创建可访问的原生文件对象 /// 创建基于当前字节数据的原生文件对象
/// </summary> /// </summary>
/// <returns>创建得到的原生文件对象</returns> /// <returns>创建得到的原生文件对象</returns>
public RawFileObject CreateRawFileObject() public RawFileObject CreateRawFileObject()
{ {
return RawFileObject.CreateFromBytes(_bytes); if (_bytes == null)
throw new InvalidOperationException($"{nameof(RawBundle)} has been unloaded.");
if (_cachedObject == null)
_cachedObject = RawFileObject.CreateFromBytes(_bytes);
return _cachedObject;
} }
/// <summary> /// <summary>
@@ -27,6 +39,12 @@ namespace YooAsset
/// </summary> /// </summary>
public void Unload() public void Unload()
{ {
if (_cachedObject != null)
{
_cachedObject.Release();
UnityEngine.Object.Destroy(_cachedObject);
_cachedObject = null;
}
_bytes = null; _bytes = null;
} }
} }

View File

@@ -5,7 +5,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 原生资源包句柄 /// 原生资源包句柄
/// </summary> /// </summary>
internal class RawBundleHandle : IBundleHandle internal sealed class RawBundleHandle : IBundleHandle
{ {
private readonly string _bundleFilePath; private readonly string _bundleFilePath;
private readonly PackageBundle _packageBundle; private readonly PackageBundle _packageBundle;
@@ -19,6 +19,12 @@ namespace YooAsset
get { return _bundleFilePath; } get { return _bundleFilePath; }
} }
/// <summary>
/// 创建原生资源包句柄实例
/// </summary>
/// <param name="bundleFilePath">资源包文件的本地路径</param>
/// <param name="packageBundle">资源包描述</param>
/// <param name="rawBundle">已加载的原生资源包数据对象</param>
public RawBundleHandle(string bundleFilePath, PackageBundle packageBundle, RawBundle rawBundle) public RawBundleHandle(string bundleFilePath, PackageBundle packageBundle, RawBundle rawBundle)
{ {
_bundleFilePath = bundleFilePath; _bundleFilePath = bundleFilePath;

View File

@@ -1,10 +1,11 @@
using System;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 用于访问原生文件字节数据及文本内容的对象 /// 原生文件对象
/// </summary> /// </summary>
public class RawFileObject : ScriptableObject public class RawFileObject : ScriptableObject
{ {
@@ -12,16 +13,23 @@ namespace YooAsset
private string _fileText; private string _fileText;
/// <summary> /// <summary>
/// 原生文件的字节数据 /// 获取原生文件的字节数据
/// </summary> /// </summary>
public byte[] Bytes => _fileBytes; /// <returns>原生文件字节数据的副本</returns>
public byte[] GetBytes()
{
if (_fileBytes == null)
return null;
var copy = new byte[_fileBytes.Length];
System.Buffer.BlockCopy(_fileBytes, 0, copy, 0, _fileBytes.Length);
return copy;
}
/// <summary> /// <summary>
/// UTF-8编码解析得到的文本内容 /// 获取以 UTF-8 编码解析的文本内容
/// </summary> /// </summary>
public string Text /// <returns>解析后的文本字符串</returns>
{ public string GetText()
get
{ {
if (_fileBytes == null || _fileBytes.Length == 0) if (_fileBytes == null || _fileBytes.Length == 0)
return null; return null;
@@ -30,15 +38,41 @@ namespace YooAsset
_fileText = Encoding.UTF8.GetString(_fileBytes); _fileText = Encoding.UTF8.GetString(_fileBytes);
return _fileText; return _fileText;
} }
#if UNITY_2021_2_OR_NEWER
/// <summary>
/// 获取原生文件的只读字节数据
/// </summary>
/// <remarks>
/// 返回的数据直接引用内部数组,调用方不应在资源包卸载后继续使用。
/// </remarks>
/// <returns>原生文件的只读字节数据</returns>
public ReadOnlySpan<byte> GetBytesAsReadOnlySpan()
{
if (_fileBytes == null)
return ReadOnlySpan<byte>.Empty;
return new ReadOnlySpan<byte>(_fileBytes);
}
#endif
/// <summary>
/// 释放内部持有的字节数据和文本缓存
/// </summary>
internal void Release()
{
_fileBytes = null;
_fileText = null;
} }
/// <summary> /// <summary>
/// 根据字节数据创建原生文件对象实例 /// 创建基于指定字节数据原生文件对象实例
/// </summary> /// </summary>
/// <param name="bytes">原生文件的字节数据</param> /// <param name="bytes">原生文件的字节数据</param>
/// <returns>创建得到的原生文件对象</returns> /// <returns>创建得到的原生文件对象</returns>
public static RawFileObject CreateFromBytes(byte[] bytes) internal static RawFileObject CreateFromBytes(byte[] bytes)
{ {
if (bytes == null)
throw new ArgumentNullException(nameof(bytes));
var obj = CreateInstance<RawFileObject>(); var obj = CreateInstance<RawFileObject>();
obj._fileBytes = bytes; obj._fileBytes = bytes;
return obj; return obj;

View File

@@ -3,11 +3,11 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 虚拟资源包的加载所有资源操作 /// 通过 AssetDatabase 加载全部资源对象(仅编辑器模式)
/// </summary> /// </summary>
internal class VBHLoadAllAssetsOperation : BHLoadAllAssetsOperation internal sealed class VBHLoadAllAssetsOperation : BHLoadAllAssetsOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -20,6 +20,11 @@ namespace YooAsset
private readonly AssetInfo _assetInfo; private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetInfo">待加载资源信息</param>
public VBHLoadAllAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo) public VBHLoadAllAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -47,8 +52,8 @@ namespace YooAsset
if (string.IsNullOrEmpty(guid)) if (string.IsNullOrEmpty(guid))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Asset not found: {_assetInfo.AssetPath}"); SetError($"Asset not found: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
return; return;
} }
@@ -90,13 +95,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load all assets: {_assetInfo.AssetPath} AssetType: null"; error = $"Failed to load all assets: '{_assetInfo.AssetPath}' AssetType: null.";
else else
error = $"Failed to load all assets: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType}"; error = $"Failed to load all assets: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType}.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(error); YooLogger.LogError(error);
} }
else else
{ {

View File

@@ -2,11 +2,11 @@
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 虚拟资源包的加载单个资源操作 /// 通过 AssetDatabase 加载单个资源对象(仅编辑器模式)
/// </summary> /// </summary>
internal class VBHLoadAssetOperation : BHLoadAssetOperation internal sealed class VBHLoadAssetOperation : BHLoadAssetOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -19,6 +19,11 @@ namespace YooAsset
private readonly AssetInfo _assetInfo; private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetInfo">待加载资源信息</param>
public VBHLoadAssetOperation(PackageBundle packageBundle, AssetInfo assetInfo) public VBHLoadAssetOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -46,8 +51,8 @@ namespace YooAsset
if (string.IsNullOrEmpty(guid)) if (string.IsNullOrEmpty(guid))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Asset not found: {_assetInfo.AssetPath}"); SetError($"Asset not found: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
return; return;
} }
@@ -69,13 +74,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load asset object: {_assetInfo.AssetPath} AssetType: null"; error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: null.";
else else
error = $"Failed to load asset object: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType}"; error = $"Failed to load asset: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType}.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(error); YooLogger.LogError(error);
} }
else else
{ {

View File

@@ -4,11 +4,11 @@ using UnityEngine.SceneManagement;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 虚拟资源包的场景加载操作 /// 通过 EditorSceneManager 加载场景资源(仅编辑器模式)
/// </summary> /// </summary>
internal class VBHLoadSceneOperation : BHLoadSceneOperation internal sealed class VBHLoadSceneOperation : BHLoadSceneOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
LoadScene, LoadScene,
@@ -17,15 +17,21 @@ namespace YooAsset
} }
private readonly AssetInfo _assetInfo; private readonly AssetInfo _assetInfo;
private readonly LoadSceneParameters _loadParams; private readonly LoadSceneParameters _loadSceneParams;
private bool _allowSceneActivation; private bool _allowSceneActivation;
private AsyncOperation _asyncOperation; private AsyncOperation _asyncOperation;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
public VBHLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadParams, bool allowSceneActivation) /// <summary>
/// 创建实例
/// </summary>
/// <param name="assetInfo">待加载场景信息</param>
/// <param name="loadSceneParams">控制场景加载的选项</param>
/// <param name="allowSceneActivation">是否允许场景在加载完成后立即激活</param>
public VBHLoadSceneOperation(AssetInfo assetInfo, LoadSceneParameters loadSceneParams, bool allowSceneActivation)
{ {
_assetInfo = assetInfo; _assetInfo = assetInfo;
_loadParams = loadParams; _loadSceneParams = loadSceneParams;
_allowSceneActivation = allowSceneActivation; _allowSceneActivation = allowSceneActivation;
} }
protected override void InternalStart() protected override void InternalStart()
@@ -47,12 +53,12 @@ namespace YooAsset
{ {
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
Result = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneInPlayMode(_assetInfo.AssetPath, _loadParams); Result = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneInPlayMode(_assetInfo.AssetPath, _loadSceneParams);
_steps = ESteps.CheckResult; _steps = ESteps.CheckResult;
} }
else else
{ {
_asyncOperation = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(_assetInfo.AssetPath, _loadParams); _asyncOperation = UnityEditor.SceneManagement.EditorSceneManager.LoadSceneAsyncInPlayMode(_assetInfo.AssetPath, _loadSceneParams);
if (_asyncOperation != null) if (_asyncOperation != null)
{ {
_asyncOperation.allowSceneActivation = _allowSceneActivation; _asyncOperation.allowSceneActivation = _allowSceneActivation;
@@ -63,9 +69,8 @@ namespace YooAsset
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Failed to load scene: {_assetInfo.AssetPath}"); SetError($"Failed to load scene: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
return;
} }
} }
} }
@@ -77,7 +82,7 @@ namespace YooAsset
if (IsWaitForCompletion) if (IsWaitForCompletion)
{ {
// 注意:场景加载无法强制异步转同步 // 注意:场景加载无法强制异步转同步
YooLogger.Error("The scene is already loading asynchronously."); YooLogger.LogError("The scene is already loading asynchronously.");
} }
else else
{ {
@@ -102,8 +107,8 @@ namespace YooAsset
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"The loaded scene is invalid: {_assetInfo.AssetPath}"); SetError($"Loaded scene is invalid: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
} }
} }
#endif #endif
@@ -113,7 +118,7 @@ namespace YooAsset
//注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法! //注意:场景加载不支持异步转同步,为了支持同步加载方法需要实现该方法!
ExecuteOnce(); ExecuteOnce();
} }
public override void AllowSceneActivation() protected override void InternalAllowSceneActivation()
{ {
_allowSceneActivation = true; _allowSceneActivation = true;
} }

View File

@@ -3,11 +3,11 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary> /// <summary>
/// 虚拟资源包的加载子资源操作 /// 通过 AssetDatabase 加载指定资源的全部子资源对象(仅编辑器模式)
/// </summary> /// </summary>
internal class VBHLoadSubAssetsOperation : BHLoadSubAssetsOperation internal sealed class VBHLoadSubAssetsOperation : BHLoadSubAssetsOperation
{ {
protected enum ESteps private enum ESteps
{ {
None, None,
CheckBundle, CheckBundle,
@@ -20,6 +20,11 @@ namespace YooAsset
private readonly AssetInfo _assetInfo; private readonly AssetInfo _assetInfo;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
/// <summary>
/// 创建实例
/// </summary>
/// <param name="packageBundle">资源包描述</param>
/// <param name="assetInfo">待加载资源信息</param>
public VBHLoadSubAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo) public VBHLoadSubAssetsOperation(PackageBundle packageBundle, AssetInfo assetInfo)
{ {
_packageBundle = packageBundle; _packageBundle = packageBundle;
@@ -47,8 +52,8 @@ namespace YooAsset
if (string.IsNullOrEmpty(guid)) if (string.IsNullOrEmpty(guid))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
SetError($"Asset not found: {_assetInfo.AssetPath}"); SetError($"Asset not found: '{_assetInfo.AssetPath}'.");
YooLogger.Error(Error); YooLogger.LogError(Error);
return; return;
} }
@@ -81,13 +86,13 @@ namespace YooAsset
{ {
string error; string error;
if (_assetInfo.AssetType == null) if (_assetInfo.AssetType == null)
error = $"Failed to load sub-assets: {_assetInfo.AssetPath} AssetType: null"; error = $"Failed to load sub-assets: '{_assetInfo.AssetPath}' AssetType: null.";
else else
error = $"Failed to load sub-assets: {_assetInfo.AssetPath} AssetType: {_assetInfo.AssetType}"; error = $"Failed to load sub-assets: '{_assetInfo.AssetPath}' AssetType: {_assetInfo.AssetType}.";
_steps = ESteps.Done; _steps = ESteps.Done;
SetError(error); SetError(error);
YooLogger.Error(error); YooLogger.LogError(error);
} }
else else
{ {

View File

@@ -5,7 +5,7 @@ namespace YooAsset
/// <summary> /// <summary>
/// 虚拟资源包句柄 /// 虚拟资源包句柄
/// </summary> /// </summary>
internal class VirtualBundleHandle : IBundleHandle internal sealed class VirtualBundleHandle : IBundleHandle
{ {
private readonly string _bundleFilePath; private readonly string _bundleFilePath;
private readonly PackageBundle _packageBundle; private readonly PackageBundle _packageBundle;
@@ -18,6 +18,11 @@ namespace YooAsset
get { return _bundleFilePath; } get { return _bundleFilePath; }
} }
/// <summary>
/// 创建虚拟资源包句柄实例
/// </summary>
/// <param name="bundleFilePath">资源包文件的本地路径</param>
/// <param name="packageBundle">资源包描述</param>
public VirtualBundleHandle(string bundleFilePath, PackageBundle packageBundle) public VirtualBundleHandle(string bundleFilePath, PackageBundle packageBundle)
{ {
_bundleFilePath = bundleFilePath; _bundleFilePath = bundleFilePath;
@@ -25,7 +30,7 @@ namespace YooAsset
} }
/// <summary> /// <summary>
/// 虚拟资源包无需执行实际卸载操作 /// 释放虚拟资源包无需执行实际卸载操作
/// </summary> /// </summary>
public void UnloadBundle() public void UnloadBundle()
{ {

View File

@@ -8,7 +8,7 @@ namespace YooAsset
/// 诊断行为组件 /// 诊断行为组件
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 负责接收 Editor 命令并发送诊断数据 /// 负责接收 Editor 命令并发送诊断数据
/// </remarks> /// </remarks>
internal class DiagnosticBehaviour : MonoBehaviour internal class DiagnosticBehaviour : MonoBehaviour
{ {
@@ -16,13 +16,13 @@ namespace YooAsset
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void OnRuntimeInitialize() private static void OnRuntimeInitialize()
{ {
_sampleOnce = false; s_sampleOnce = false;
_autoSampling = false; s_autoSampling = false;
} }
#endif #endif
private static bool _sampleOnce = false; private static bool s_sampleOnce = false;
private static bool _autoSampling = false; private static bool s_autoSampling = false;
private void Awake() private void Awake()
{ {
@@ -48,9 +48,9 @@ namespace YooAsset
} }
private void LateUpdate() private void LateUpdate()
{ {
if (_autoSampling || _sampleOnce) if (s_autoSampling || s_sampleOnce)
{ {
_sampleOnce = false; s_sampleOnce = false;
var debugReport = YooAssets.GetDiagnosticReport(); var debugReport = YooAssets.GetDiagnosticReport();
var data = DiagnosticReport.Serialize(debugReport); var data = DiagnosticReport.Serialize(debugReport);
@@ -65,21 +65,21 @@ namespace YooAsset
private static void HandleEditorMessage(MessageEventArgs args) private static void HandleEditorMessage(MessageEventArgs args)
{ {
var command = DiagnosticCommand.Deserialize(args.data); var command = DiagnosticCommand.Deserialize(args.data);
YooLogger.Log($"[{nameof(DiagnosticBehaviour)}] Handle command: Type={command.CommandType}, Param={command.Parameter}"); YooLogger.Log($"[{nameof(DiagnosticBehaviour)}] Received command: Type={command.CommandType}, Param='{command.Parameter}'.");
if (command.CommandType == (int)EDiagnosticCommandType.SampleOnce) if (command.CommandType == EDiagnosticCommandType.SampleOnce)
{ {
_sampleOnce = true; s_sampleOnce = true;
} }
else if (command.CommandType == (int)EDiagnosticCommandType.AutoSampling) else if (command.CommandType == EDiagnosticCommandType.AutoSampling)
{ {
if (command.Parameter == "open") if (command.Parameter == DiagnosticSystemConsts.AutoSamplingOpen)
_autoSampling = true; s_autoSampling = true;
else else
_autoSampling = false; s_autoSampling = false;
} }
else else
{ {
YooLogger.Warning($"Unknown diagnostic command type: {command.CommandType}."); YooLogger.LogWarning($"Unknown diagnostic command type: {command.CommandType}.");
} }
} }
} }

View File

@@ -8,7 +8,7 @@ namespace YooAsset
/// 诊断命令 /// 诊断命令
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// 用于 Editor 向 Player 发送诊断指令 /// 用于 Editor 向 Player 发送诊断指令
/// </remarks> /// </remarks>
[Serializable] [Serializable]
internal class DiagnosticCommand internal class DiagnosticCommand
@@ -16,14 +16,13 @@ namespace YooAsset
/// <summary> /// <summary>
/// 命令类型 /// 命令类型
/// </summary> /// </summary>
public int CommandType; public EDiagnosticCommandType CommandType;
/// <summary> /// <summary>
/// 命令附加参数 /// 命令附加参数
/// </summary> /// </summary>
public string Parameter; public string Parameter;
/// <summary> /// <summary>
/// 序列化命令为字节数组 /// 序列化命令为字节数组
/// </summary> /// </summary>

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
namespace YooAsset namespace YooAsset
@@ -8,7 +7,7 @@ namespace YooAsset
/// 描述资源包的运行时诊断信息 /// 描述资源包的运行时诊断信息
/// </summary> /// </summary>
[Serializable] [Serializable]
internal struct DiagnosticBundleInfo : IComparer<DiagnosticBundleInfo>, IComparable<DiagnosticBundleInfo> internal class DiagnosticBundleInfo : IComparable<DiagnosticBundleInfo>
{ {
/// <summary> /// <summary>
/// 资源包名称 /// 资源包名称
@@ -30,13 +29,10 @@ namespace YooAsset
/// </summary> /// </summary>
public List<string> Referencers; public List<string> Referencers;
/// <inheritdoc />
public int CompareTo(DiagnosticBundleInfo other) public int CompareTo(DiagnosticBundleInfo other)
{ {
return Compare(this, other); return string.CompareOrdinal(BundleName, other.BundleName);
}
public int Compare(DiagnosticBundleInfo a, DiagnosticBundleInfo b)
{
return string.CompareOrdinal(a.BundleName, b.BundleName);
} }
} }
} }

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