修正 P1 资源后端泛型契约
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
### 本轮 P1 包含
|
### 本轮 P1 包含
|
||||||
|
|
||||||
1. 资源生产化第一步:引入 `IResourceBackend`,将 Addressables 反射适配从 `FlowScope.Runtime` 拆到 `FlowScope.Addressables`。
|
1. 资源生产化第一步:引入泛型 `IResourceBackend`,将 Addressables 适配从 `FlowScope.Runtime` 拆到 `FlowScope.Addressables`,并移除 Addressables 反射调用。
|
||||||
2. 配置扩展点:引入 `IConfigSource` 与 `IConfigParser`,让 JSON provider 从“硬编码 JSON 文本映射”变成“source + parser”的组合。
|
2. 配置扩展点:引入 `IConfigSource` 与 `IConfigParser`,让 JSON provider 从“硬编码 JSON 文本映射”变成“source + parser”的组合。
|
||||||
3. 存档迁移扩展点:引入 `ISaveMigration` 与带版本包裹的 JSON serializer,支持单 key 数据从旧版本迁移到当前版本。
|
3. 存档迁移扩展点:引入 `ISaveMigration` 与带版本包裹的 JSON serializer,支持单 key 数据从旧版本迁移到当前版本。
|
||||||
4. UI 生产化第一步:引入轻量 `IUIScreenNavigator` 与 panel 预加载服务,仍保持 `UIManager` 的层内 LIFO 规则。
|
4. UI 生产化第一步:引入轻量 `IUIScreenNavigator` 与 panel 预加载服务,仍保持 `UIManager` 的层内 LIFO 规则。
|
||||||
@@ -82,7 +82,6 @@
|
|||||||
Create `My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs`:
|
Create `My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs`:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -92,12 +91,13 @@ namespace FlowScope.Resources
|
|||||||
{
|
{
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
|
|
||||||
Task<object> LoadAsync(
|
Task<T> LoadAsync<T>(
|
||||||
string key,
|
string key,
|
||||||
Type assetType,
|
CancellationToken cancellationToken)
|
||||||
CancellationToken cancellationToken);
|
where T : class;
|
||||||
|
|
||||||
void Release(string key, Type assetType, object asset);
|
void Release<T>(string key, T asset)
|
||||||
|
where T : class;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -269,10 +269,11 @@ git commit -m "定义 Game Core P1 生产化契约"
|
|||||||
**Goal:** 让核心资源服务只依赖 `IResourceBackend`,将 Addressables 具体依赖放进独立 asmdef,降低 Runtime 核心升级风险。
|
**Goal:** 让核心资源服务只依赖 `IResourceBackend`,将 Addressables 具体依赖放进独立 asmdef,降低 Runtime 核心升级风险。
|
||||||
|
|
||||||
**Owned files:**
|
**Owned files:**
|
||||||
- Modify: `My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs`
|
- Delete: `My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs`
|
||||||
- Create: `My project/Assets/FlowScope/Runtime/Resources/ResourceService.cs`
|
- Create: `My project/Assets/FlowScope/Runtime/Resources/ResourceService.cs`
|
||||||
- Create: `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef`
|
- Create: `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef`
|
||||||
- Create: `My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs`
|
- Create: `My project/Assets/FlowScope/Addressables/AddressablesResourceBackend.cs`
|
||||||
|
- Create: `My project/Assets/FlowScope/Addressables/AddressablesResourceService.cs`
|
||||||
- Create: `My project/Assets/FlowScope/Tests/EditMode/Resources/ResourceServiceBackendTests.cs`
|
- Create: `My project/Assets/FlowScope/Tests/EditMode/Resources/ResourceServiceBackendTests.cs`
|
||||||
- Create: `My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs`
|
- Create: `My project/Assets/FlowScope/Tests/PlayMode/Resources/AddressablesResourceBackendTests.cs`
|
||||||
- Modify: `My project/Assets/FlowScope/Tests/PlayMode/FlowScope.Tests.PlayMode.asmdef`
|
- Modify: `My project/Assets/FlowScope/Tests/PlayMode/FlowScope.Tests.PlayMode.asmdef`
|
||||||
@@ -309,13 +310,15 @@ private sealed class FakeResourceBackend : IResourceBackend
|
|||||||
public int ReleaseCalls { get; private set; }
|
public int ReleaseCalls { get; private set; }
|
||||||
public TaskCompletionSource<object> Gate { get; } = new();
|
public TaskCompletionSource<object> Gate { get; } = new();
|
||||||
|
|
||||||
public async Task<object> LoadAsync(string key, Type assetType, CancellationToken cancellationToken)
|
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
LoadCalls++;
|
LoadCalls++;
|
||||||
return await Gate.Task;
|
return (T)await Gate.Task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Release(string key, Type assetType, object asset)
|
public void Release<T>(string key, T asset)
|
||||||
|
where T : class
|
||||||
{
|
{
|
||||||
ReleaseCalls++;
|
ReleaseCalls++;
|
||||||
}
|
}
|
||||||
@@ -343,29 +346,56 @@ Keep P0 semantics:
|
|||||||
- Last handle disposal calls backend release.
|
- Last handle disposal calls backend release.
|
||||||
- Canceled waiter does not prevent other waiters from completing.
|
- Canceled waiter does not prevent other waiters from completing.
|
||||||
- If all waiters cancel before backend completes, release completed asset when backend eventually resolves.
|
- If all waiters cancel before backend completes, release completed asset when backend eventually resolves.
|
||||||
|
- `ResourceService` must call `IResourceBackend.LoadAsync<T>` directly. Do not use reflection to bridge `Type` to generic calls.
|
||||||
|
|
||||||
- [ ] **Step 3: Convert `AddressablesResourceService` into compatibility wrapper**
|
- [ ] **Step 3: Move `AddressablesResourceService` compatibility wrapper to adapter assembly**
|
||||||
|
|
||||||
Modify `AddressablesResourceService` to preserve existing API:
|
Delete or empty the old Runtime implementation file:
|
||||||
|
|
||||||
|
```text
|
||||||
|
My project/Assets/FlowScope/Runtime/Resources/AddressablesResourceService.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a compatibility wrapper in the Addressables adapter assembly:
|
||||||
|
|
||||||
|
```text
|
||||||
|
My project/Assets/FlowScope/Addressables/AddressablesResourceService.cs
|
||||||
|
```
|
||||||
|
|
||||||
|
Use namespace `FlowScope.Resources` to preserve existing call sites:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace FlowScope.Resources
|
||||||
{
|
{
|
||||||
private readonly ResourceService _inner;
|
public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService
|
||||||
|
|
||||||
public AddressablesResourceService()
|
|
||||||
: this(new AddressablesResourceBackend())
|
|
||||||
{
|
{
|
||||||
}
|
private readonly ResourceService _inner;
|
||||||
|
|
||||||
public AddressablesResourceService(IResourceBackend backend)
|
public AddressablesResourceService()
|
||||||
{
|
: this(new AddressablesResourceBackend())
|
||||||
_inner = new ResourceService(backend);
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressablesResourceService(IResourceBackend backend)
|
||||||
|
{
|
||||||
|
_inner = new ResourceService(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IResourceHandle<T>> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
||||||
|
where T : class => _inner.LoadAsync<T>(key, cancellationToken);
|
||||||
|
|
||||||
|
public IResourceGroup CreateGroup() => _inner.CreateGroup();
|
||||||
|
|
||||||
|
public bool TryLoadCompleted<T>(string key, out IResourceHandle<T> handle)
|
||||||
|
where T : class => _inner.TryLoadCompleted(key, out handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If `AddressablesResourceBackend` lives in a separate asmdef and cannot be referenced by Runtime, keep this wrapper in `FlowScope.Addressables` instead and update usages in sample/integration. Do not keep reflection backend in `FlowScope.Runtime`.
|
Do not keep an Addressables reflection backend in `FlowScope.Runtime`.
|
||||||
|
|
||||||
- [ ] **Step 4: Create Addressables asmdef**
|
- [ ] **Step 4: Create Addressables asmdef**
|
||||||
|
|
||||||
@@ -393,10 +423,9 @@ Create `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef`:
|
|||||||
|
|
||||||
- [ ] **Step 5: Implement typed Addressables backend**
|
- [ ] **Step 5: Implement typed Addressables backend**
|
||||||
|
|
||||||
Create `AddressablesResourceBackend` using direct API references:
|
Create `AddressablesResourceBackend` using direct typed Addressables API references. This backend must not use reflection:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FlowScope.Resources;
|
using FlowScope.Resources;
|
||||||
@@ -408,25 +437,19 @@ namespace FlowScope.Addressables
|
|||||||
{
|
{
|
||||||
public string Name => "Addressables";
|
public string Name => "Addressables";
|
||||||
|
|
||||||
public async Task<object> LoadAsync(string key, Type assetType, CancellationToken cancellationToken)
|
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
||||||
{
|
where T : class
|
||||||
var method = typeof(AddressablesResourceBackend)
|
|
||||||
.GetMethod(nameof(LoadTypedAsync), System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic)
|
|
||||||
.MakeGenericMethod(assetType);
|
|
||||||
return await (Task<object>)method.Invoke(null, new object[] { key, cancellationToken });
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Release(string key, Type assetType, object asset)
|
|
||||||
{
|
|
||||||
Addressables.Release(asset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<object> LoadTypedAsync<T>(string key, CancellationToken cancellationToken)
|
|
||||||
{
|
{
|
||||||
var handle = Addressables.LoadAssetAsync<T>(key);
|
var handle = Addressables.LoadAssetAsync<T>(key);
|
||||||
await WaitWithCancellation(handle.Task, cancellationToken);
|
await WaitWithCancellation(handle.Task, cancellationToken);
|
||||||
return handle.Result;
|
return handle.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Release<T>(string key, T asset)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
Addressables.Release(asset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user