mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
docs(config): 添加游戏内容配置系统完整文档与集成测试
- 新增游戏内容配置系统完整使用文档,涵盖 YAML 配置、JSON Schema 结构、目录组织等 - 添加推荐的项目接入模板,包括目录结构、csproj 配置、启动帮助器和运行时读取入口 - 实现官方启动帮助器 GameConfigBootstrap 与 GameConfigBootstrapOptions 集成 - 添加强类型配置表访问、查询辅助和跨表引用功能说明 - 实现开发期热重载、运行时校验和 VS Code 工具集成指南 - 添加完整的单元测试验证生成器绑定、聚合注册和强类型访问功能 - 提供 Architecture 接入模板和热重载管理最佳实践 - 包含运行时校验行为、诊断对象和配置加载异常处理说明
This commit is contained in:
parent
5190ba2359
commit
fac2453766
@ -127,10 +127,21 @@ public class GeneratedConfigConsumerIntegrationTests
|
|||||||
{
|
{
|
||||||
IncludedTableNames = new[] { ItemConfigBindings.TableName }
|
IncludedTableNames = new[] { ItemConfigBindings.TableName }
|
||||||
});
|
});
|
||||||
|
var predicateOnlyRegistrationTables = GeneratedConfigCatalog.GetTablesForRegistration(
|
||||||
|
new GeneratedConfigRegistrationOptions
|
||||||
|
{
|
||||||
|
TableFilter = static metadata =>
|
||||||
|
string.Equals(metadata.TableName, MonsterConfigBindings.TableName, StringComparison.Ordinal)
|
||||||
|
});
|
||||||
var monsterOnlyOptions = new GeneratedConfigRegistrationOptions
|
var monsterOnlyOptions = new GeneratedConfigRegistrationOptions
|
||||||
{
|
{
|
||||||
IncludedConfigDomains = new[] { MonsterConfigBindings.ConfigDomain }
|
IncludedConfigDomains = new[] { MonsterConfigBindings.ConfigDomain }
|
||||||
};
|
};
|
||||||
|
var predicateOnlyOptions = new GeneratedConfigRegistrationOptions
|
||||||
|
{
|
||||||
|
TableFilter = static metadata =>
|
||||||
|
string.Equals(metadata.TableName, MonsterConfigBindings.TableName, StringComparison.Ordinal)
|
||||||
|
};
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
@ -139,10 +150,14 @@ public class GeneratedConfigConsumerIntegrationTests
|
|||||||
Assert.That(missingDomainTables, Is.Empty);
|
Assert.That(missingDomainTables, Is.Empty);
|
||||||
Assert.That(itemOnlyRegistrationTables.Select(static metadata => metadata.TableName),
|
Assert.That(itemOnlyRegistrationTables.Select(static metadata => metadata.TableName),
|
||||||
Is.EqualTo(new[] { ItemConfigBindings.TableName }));
|
Is.EqualTo(new[] { ItemConfigBindings.TableName }));
|
||||||
|
Assert.That(predicateOnlyRegistrationTables.Select(static metadata => metadata.TableName),
|
||||||
|
Is.EqualTo(new[] { MonsterConfigBindings.TableName }));
|
||||||
Assert.That(GeneratedConfigCatalog.GetTablesForRegistration().Select(static metadata => metadata.TableName),
|
Assert.That(GeneratedConfigCatalog.GetTablesForRegistration().Select(static metadata => metadata.TableName),
|
||||||
Is.SupersetOf(new[] { ItemConfigBindings.TableName, MonsterConfigBindings.TableName }));
|
Is.SupersetOf(new[] { ItemConfigBindings.TableName, MonsterConfigBindings.TableName }));
|
||||||
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(monsterMetadata, monsterOnlyOptions), Is.True);
|
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(monsterMetadata, monsterOnlyOptions), Is.True);
|
||||||
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(itemMetadata, monsterOnlyOptions), Is.False);
|
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(itemMetadata, monsterOnlyOptions), Is.False);
|
||||||
|
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(monsterMetadata, predicateOnlyOptions), Is.True);
|
||||||
|
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(itemMetadata, predicateOnlyOptions), Is.False);
|
||||||
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(monsterMetadata, options: null), Is.True);
|
Assert.That(GeneratedConfigCatalog.MatchesRegistrationOptions(monsterMetadata, options: null), Is.True);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -404,7 +404,7 @@ if (monsterTable.TryFindFirstByFaction("dungeon", out var firstDungeonMonster))
|
|||||||
|
|
||||||
### Architecture 推荐接入模板
|
### Architecture 推荐接入模板
|
||||||
|
|
||||||
如果你的项目已经基于 `GFramework.Core.Architectures.Architecture` 组织初始化流程,推荐把配置系统接到 `OnInitialize()` 阶段,并把 `GameConfigBootstrap.Registry` 注册为 utility:
|
如果你的项目已经基于 `GFramework.Core.Architectures.Architecture` 组织初始化流程,并且当前宿主没有提供更上层的异步启动入口,可以把配置系统接到 `OnInitialize()` 阶段,并把 `GameConfigBootstrap.Registry` 注册为 utility:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
using GFramework.Core.Architectures;
|
using GFramework.Core.Architectures;
|
||||||
@ -440,6 +440,12 @@ public sealed class GameArchitecture : Architecture
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
这个模板里的 `.GetAwaiter().GetResult()` 只是“同步 `OnInitialize()` 到异步配置加载”的桥接写法,不应被理解为无条件推荐:
|
||||||
|
|
||||||
|
- 如果宿主已经提供异步组合根、启动器或更早的异步初始化阶段,优先在那里直接 `await _configBootstrap.InitializeAsync()`
|
||||||
|
- 只有在 `Architecture` 只暴露同步 `OnInitialize()`,且当前线程不存在需要恢复的 `SynchronizationContext` 时,才适合使用这类同步桥接
|
||||||
|
- 在 UI 线程、ASP.NET Classic 等存在活动 `SynchronizationContext` 的环境中,不要直接阻塞等待异步初始化;应把配置初始化前移到异步入口,或改为由不受该上下文约束的启动线程完成
|
||||||
|
|
||||||
初始化完成后,业务组件可以继续通过架构上下文读取 utility,再走生成的强类型入口:
|
初始化完成后,业务组件可以继续通过架构上下文读取 utility,再走生成的强类型入口:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
@ -590,7 +596,7 @@ var loader = new YamlConfigLoader("config-root")
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
如果你想在真正调用 `RegisterAllGeneratedConfigTables(...)` 之前,先把“这次会注册哪些表”打到日志里,推荐直接复用同一份 options 做启动诊断,而不是手写一套平行筛选逻辑:
|
如果你想在真正调用 `RegisterAllGeneratedConfigTables(...)` 之前,先把“这次会注册哪些表”输出到日志中,推荐直接复用同一份 options 做启动诊断,而不是手写一套平行筛选逻辑:
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
var registrationOptions = new GeneratedConfigRegistrationOptions
|
var registrationOptions = new GeneratedConfigRegistrationOptions
|
||||||
@ -610,7 +616,7 @@ var loader = new YamlConfigLoader("config-root")
|
|||||||
这里的规则是:
|
这里的规则是:
|
||||||
|
|
||||||
- `IncludedConfigDomains` 与 `IncludedTableNames` 都按 `StringComparison.Ordinal` 做白名单匹配;传 `null` 或空集合表示“不限制”
|
- `IncludedConfigDomains` 与 `IncludedTableNames` 都按 `StringComparison.Ordinal` 做白名单匹配;传 `null` 或空集合表示“不限制”
|
||||||
- `TableFilter` 会在上述白名单通过后执行,适合继续按 schema 路径、配置目录等元数据做更细的启动裁剪
|
- `TableFilter` 会在上述白名单通过后执行,适合继续按 schema 路径、配置目录等元数据做更细粒度的启动裁剪
|
||||||
- `GeneratedConfigCatalog.GetTablesForRegistration(...)` 与 `RegisterAllGeneratedConfigTables(...)` 复用同一套筛选规则,便于在启动日志和真实注册之间保持一致
|
- `GeneratedConfigCatalog.GetTablesForRegistration(...)` 与 `RegisterAllGeneratedConfigTables(...)` 复用同一套筛选规则,便于在启动日志和真实注册之间保持一致
|
||||||
- 未显式配置 comparer 的表,仍然使用各自 `Register{Entity}Table()` 的默认行为
|
- 未显式配置 comparer 的表,仍然使用各自 `Register{Entity}Table()` 的默认行为
|
||||||
- 需要自定义 comparer 的表,可以通过 `GeneratedConfigRegistrationOptions` 按表覆盖
|
- 需要自定义 comparer 的表,可以通过 `GeneratedConfigRegistrationOptions` 按表覆盖
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user