From f855c34bf90824212651e0e42f0d4791cd328690 Mon Sep 17 00:00:00 2001 From: "JSD\\13999" <1399945104@qq.com> Date: Wed, 20 May 2026 15:28:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=20P1=20=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=B3=9B=E5=9E=8B=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-05-20-p1-production-hardening-plan.md | 99 ++++++++++++------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/docs/superpowers/plans/2026-05-20-p1-production-hardening-plan.md b/docs/superpowers/plans/2026-05-20-p1-production-hardening-plan.md index c1b9d1a..3a8d254 100644 --- a/docs/superpowers/plans/2026-05-20-p1-production-hardening-plan.md +++ b/docs/superpowers/plans/2026-05-20-p1-production-hardening-plan.md @@ -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 LoadAsync( + Task LoadAsync( string key, - Type assetType, - CancellationToken cancellationToken); + CancellationToken cancellationToken) + where T : class; - void Release(string key, Type assetType, object asset); + void Release(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 Gate { get; } = new(); - public async Task LoadAsync(string key, Type assetType, CancellationToken cancellationToken) + public async Task LoadAsync(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(string key, T asset) + where T : class { ReleaseCalls++; } @@ -343,29 +346,56 @@ 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` 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 { - private readonly ResourceService _inner; - - public AddressablesResourceService() - : this(new AddressablesResourceBackend()) + public sealed class AddressablesResourceService : IResourceService, ICompletedResourceService { - } + private readonly ResourceService _inner; - public AddressablesResourceService(IResourceBackend backend) - { - _inner = new ResourceService(backend); + public AddressablesResourceService() + : this(new AddressablesResourceBackend()) + { + } + + public AddressablesResourceService(IResourceBackend backend) + { + _inner = new ResourceService(backend); + } + + public Task> LoadAsync(string key, CancellationToken cancellationToken) + where T : class => _inner.LoadAsync(key, cancellationToken); + + public IResourceGroup CreateGroup() => _inner.CreateGroup(); + + public bool TryLoadCompleted(string key, out IResourceHandle 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 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)method.Invoke(null, new object[] { key, cancellationToken }); - } - - public void Release(string key, Type assetType, object asset) - { - Addressables.Release(asset); - } - - private static async Task LoadTypedAsync(string key, CancellationToken cancellationToken) + public async Task LoadAsync(string key, CancellationToken cancellationToken) + where T : class { var handle = Addressables.LoadAssetAsync(key); await WaitWithCancellation(handle.Task, cancellationToken); return handle.Result; } + + public void Release(string key, T asset) + where T : class + { + Addressables.Release(asset); + } } } ```