feat(config): 添加架构配置集成测试和模块实现

- 实现了 ArchitectureConfigIntegrationTests 测试类,验证配置模块在架构场景下的完整链路
- 添加了 GameConfigModule 类,提供基于 Architecture 的配置模块接入入口
- 实现了配置模块的生命周期管理,包括首次加载和热重载支持
- 集成了 BootstrapInitializationHook 确保配置在 utility 初始化前完成加载
- 添加了模块复用限制和安装窗口验证机制
- 实现了架构销毁时的资源清理和生命周期钩子注册
This commit is contained in:
GeWuYou 2026-04-09 16:33:02 +08:00
parent eb7a8c702c
commit febf948077
2 changed files with 30 additions and 26 deletions

View File

@ -1,15 +1,11 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GFramework.Core.Architectures;
using GFramework.Core.Extensions;
using GFramework.Core.Utility;
using GFramework.Game.Abstractions.Config;
using GFramework.Game.Config;
using GFramework.Game.Config.Generated;
using NUnit.Framework;
namespace GFramework.Game.Tests.Config;
@ -153,33 +149,39 @@ public class ArchitectureConfigIntegrationTests
}
/// <summary>
/// 验证配置启动帮助器在同步阻塞且存在 <see cref="SynchronizationContext" /> 的线程上仍可完成初始化,
/// 避免架构生命周期钩子的同步桥接因为 await 捕获上下文而死锁
/// 验证配置模块在同步阻塞且存在 <see cref="SynchronizationContext" /> 的线程上仍可完成架构初始化,
/// 直接覆盖 <see cref="GameConfigModule" /> 通过生命周期钩子执行同步桥接的真实路径
/// </summary>
[Test]
public void GameConfigBootstrapShouldSupportSynchronousBridgeOnBlockingSynchronizationContext()
public void GameConfigModuleShouldSupportSynchronousBridgeOnBlockingSynchronizationContext()
{
var rootPath = CreateTempConfigRoot();
GameConfigBootstrap? bootstrap = null;
ConsumerArchitecture? architecture = null;
var initialized = false;
try
{
bootstrap = CreateBootstrap(rootPath);
architecture = new ConsumerArchitecture(rootPath);
RunBlockingOnSynchronizationContext(
() => bootstrap.InitializeAsync(),
() => architecture.InitializeAsync(),
TimeSpan.FromSeconds(5));
initialized = true;
var monsterTable = bootstrap.Registry.GetMonsterTable();
var monsterTable = architecture.Registry.GetMonsterTable();
Assert.Multiple(() =>
{
Assert.That(bootstrap.IsInitialized, Is.True);
Assert.That(architecture.ConfigModule.IsInitialized, Is.True);
Assert.That(monsterTable.Get(1).Name, Is.EqualTo("Slime"));
Assert.That(monsterTable.FindByFaction("dungeon").Count(), Is.EqualTo(2));
});
}
finally
{
bootstrap?.Dispose();
if (architecture is not null && initialized)
{
architecture.DestroyAsync().GetAwaiter().GetResult();
}
DeleteDirectoryIfExists(rootPath);
}
}
@ -322,14 +324,9 @@ public class ArchitectureConfigIntegrationTests
}
}
private static GameConfigBootstrap CreateBootstrap(string configRoot)
{
return new GameConfigBootstrap(CreateBootstrapOptions(configRoot));
}
/// <summary>
/// 创建一个使用配置模块的模块实例。
/// </summary>
/// 创建一个使用配置模块的模块实例。
/// </summary>
/// <param name="configRoot">测试配置根目录。</param>
/// <returns>已配置的模块实例。</returns>
private static GameConfigModule CreateModule(string configRoot)

View File

@ -22,7 +22,7 @@ public sealed class GameConfigModule : IArchitectureModule
{
private const int InstallStateNotInstalled = 0;
private const int InstallStateInstalling = 1;
private const int InstallStateInstalled = 2;
private const int InstallStateConsumed = 2;
private readonly GameConfigBootstrap _bootstrap;
private readonly ModuleBootstrapLifetimeUtility _lifetimeUtility;
@ -76,6 +76,11 @@ public sealed class GameConfigModule : IArchitectureModule
/// <param name="architecture">目标架构实例。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="architecture" /> 为空时抛出。</exception>
/// <exception cref="InvalidOperationException">当同一个模块实例被重复安装时抛出。</exception>
/// <remarks>
/// 生命周期阶段校验会在任何注册动作前执行,因此错过安装窗口的调用不会消耗当前模块实例。
/// 一旦开始向架构注册 utility 或生命周期钩子,就不存在回滚 API因此后续任何失败都会把该模块实例视为已消耗
/// 调用方必须创建新的 <see cref="GameConfigModule" /> 再重试。
/// </remarks>
public void Install(IArchitecture architecture)
{
ArgumentNullException.ThrowIfNull(architecture);
@ -93,16 +98,18 @@ public sealed class GameConfigModule : IArchitectureModule
try
{
// 先注册生命周期钩子,确保任何“已错过 BeforeUtilityInit”的安装都会在暴露注册表之前失败
// 避免架构看到永远不会完成首次加载的半安装配置入口。
architecture.RegisterLifecycleHook(new BootstrapInitializationHook(_bootstrap));
// 阶段窗口已经在前面做过无副作用校验,因此这里优先注册 utility
// 让常见的容器/上下文接线失败在不可回滚的 hook 注册之前暴露出来。
architecture.RegisterUtility(Registry);
architecture.RegisterUtility(_lifetimeUtility);
Volatile.Write(ref _installState, InstallStateInstalled);
architecture.RegisterLifecycleHook(new BootstrapInitializationHook(_bootstrap));
Volatile.Write(ref _installState, InstallStateConsumed);
}
catch
{
Volatile.Write(ref _installState, InstallStateNotInstalled);
// 架构对 utility / hook 注册都不提供回滚入口,因此一旦进入注册阶段,
// 即使安装失败也必须禁止复用同一个模块实例,避免重复暴露共享注册表或挂上第二个 hook。
Volatile.Write(ref _installState, InstallStateConsumed);
throw;
}
}