修正 P1 资源后端泛型契约
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
|
||||
### 本轮 P1 包含
|
||||
|
||||
1. 资源生产化第一步:引入 `IResourceBackend`,将 Addressables 反射适配从 `FlowScope.Runtime` 拆到 `FlowScope.Addressables`。
|
||||
1. 资源生产化第一步:引入泛型 `IResourceBackend`,将 Addressables 适配从 `FlowScope.Runtime` 拆到 `FlowScope.Addressables`,并移除 Addressables 反射调用。
|
||||
2. 配置扩展点:引入 `IConfigSource` 与 `IConfigParser`,让 JSON provider 从“硬编码 JSON 文本映射”变成“source + parser”的组合。
|
||||
3. 存档迁移扩展点:引入 `ISaveMigration` 与带版本包裹的 JSON serializer,支持单 key 数据从旧版本迁移到当前版本。
|
||||
4. UI 生产化第一步:引入轻量 `IUIScreenNavigator` 与 panel 预加载服务,仍保持 `UIManager` 的层内 LIFO 规则。
|
||||
@@ -82,7 +82,6 @@
|
||||
Create `My project/Assets/FlowScope/Runtime/Resources/IResourceBackend.cs`:
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -92,12 +91,13 @@ namespace FlowScope.Resources
|
||||
{
|
||||
string Name { get; }
|
||||
|
||||
Task<object> LoadAsync(
|
||||
Task<T> LoadAsync<T>(
|
||||
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 核心升级风险。
|
||||
|
||||
**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/Addressables/FlowScope.Addressables.asmdef`
|
||||
- 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/PlayMode/Resources/AddressablesResourceBackendTests.cs`
|
||||
- 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 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++;
|
||||
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++;
|
||||
}
|
||||
@@ -343,14 +346,32 @@ Keep P0 semantics:
|
||||
- Last handle disposal calls backend release.
|
||||
- Canceled waiter does not prevent other waiters from completing.
|
||||
- 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
|
||||
public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FlowScope.Resources
|
||||
{
|
||||
public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService
|
||||
{
|
||||
private readonly ResourceService _inner;
|
||||
|
||||
public AddressablesResourceService()
|
||||
@@ -362,10 +383,19 @@ public sealed class AddressablesResourceService : IResourceService, ICompletedRe
|
||||
{
|
||||
_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**
|
||||
|
||||
@@ -393,10 +423,9 @@ Create `My project/Assets/FlowScope/Addressables/FlowScope.Addressables.asmdef`:
|
||||
|
||||
- [ ] **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
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using FlowScope.Resources;
|
||||
@@ -408,25 +437,19 @@ namespace FlowScope.Addressables
|
||||
{
|
||||
public string Name => "Addressables";
|
||||
|
||||
public async Task<object> LoadAsync(string key, Type assetType, CancellationToken cancellationToken)
|
||||
{
|
||||
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)
|
||||
public async Task<T> LoadAsync<T>(string key, CancellationToken cancellationToken)
|
||||
where T : class
|
||||
{
|
||||
var handle = Addressables.LoadAssetAsync<T>(key);
|
||||
await WaitWithCancellation(handle.Task, cancellationToken);
|
||||
return handle.Result;
|
||||
}
|
||||
|
||||
public void Release<T>(string key, T asset)
|
||||
where T : class
|
||||
{
|
||||
Addressables.Release(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user