mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
feat(config): 添加AI-First配置系统及文档
- 引入YAML配置源文件支持 - 实现JSON Schema结构描述功能 - 提供一对象一文件的目录组织方式 - 添加运行时只读查询能力 - 实现Source Generator生成配置类型和表包装 - 集成VS Code插件提供配置浏览和编辑功能 - 添加开发期热重载支持 - 提供跨表引用校验机制 - 创建配置生成器约定和绑定辅助类 - 添加详细的中文文档说明 - 实现集成测试验证生成器功能
This commit is contained in:
parent
ec5153f452
commit
61cc7eaa6d
@ -91,9 +91,16 @@ public class GeneratedConfigConsumerIntegrationTests
|
|||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
|
Assert.That(MonsterConfigBindings.ConfigDomain, Is.EqualTo("monster"));
|
||||||
Assert.That(MonsterConfigBindings.TableName, Is.EqualTo("monster"));
|
Assert.That(MonsterConfigBindings.TableName, Is.EqualTo("monster"));
|
||||||
Assert.That(MonsterConfigBindings.ConfigRelativePath, Is.EqualTo("monster"));
|
Assert.That(MonsterConfigBindings.ConfigRelativePath, Is.EqualTo("monster"));
|
||||||
Assert.That(MonsterConfigBindings.SchemaRelativePath, Is.EqualTo("schemas/monster.schema.json"));
|
Assert.That(MonsterConfigBindings.SchemaRelativePath, Is.EqualTo("schemas/monster.schema.json"));
|
||||||
|
Assert.That(MonsterConfigBindings.Metadata.ConfigDomain, Is.EqualTo(MonsterConfigBindings.ConfigDomain));
|
||||||
|
Assert.That(MonsterConfigBindings.Metadata.TableName, Is.EqualTo(MonsterConfigBindings.TableName));
|
||||||
|
Assert.That(MonsterConfigBindings.Metadata.ConfigRelativePath,
|
||||||
|
Is.EqualTo(MonsterConfigBindings.ConfigRelativePath));
|
||||||
|
Assert.That(MonsterConfigBindings.Metadata.SchemaRelativePath,
|
||||||
|
Is.EqualTo(MonsterConfigBindings.SchemaRelativePath));
|
||||||
Assert.That(table.Count, Is.EqualTo(2));
|
Assert.That(table.Count, Is.EqualTo(2));
|
||||||
Assert.That(table.Get(1).Name, Is.EqualTo("Slime"));
|
Assert.That(table.Get(1).Name, Is.EqualTo("Slime"));
|
||||||
Assert.That(table.Get(2).Hp, Is.EqualTo(30));
|
Assert.That(table.Get(2).Hp, Is.EqualTo(30));
|
||||||
|
|||||||
@ -9,20 +9,51 @@ namespace GFramework.Game.Config.Generated;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class MonsterConfigBindings
|
public static class MonsterConfigBindings
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Groups the schema-derived metadata constants so consumer code can reuse one stable entry point.
|
||||||
|
/// </summary>
|
||||||
|
public static class Metadata
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the logical config domain derived from the schema base name. The current runtime convention keeps this value aligned with the generated table name.
|
||||||
|
/// </summary>
|
||||||
|
public const string ConfigDomain = "monster";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the runtime registration name of the generated config table.
|
||||||
|
/// </summary>
|
||||||
|
public const string TableName = "monster";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the config directory path expected by the generated registration helper.
|
||||||
|
/// </summary>
|
||||||
|
public const string ConfigRelativePath = "monster";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the schema file path expected by the generated registration helper.
|
||||||
|
/// </summary>
|
||||||
|
public const string SchemaRelativePath = "schemas/monster.schema.json";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the logical config domain derived from the schema base name. The current runtime convention keeps this value aligned with the generated table name.
|
||||||
|
/// </summary>
|
||||||
|
public const string ConfigDomain = Metadata.ConfigDomain;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the runtime registration name of the generated config table.
|
/// Gets the runtime registration name of the generated config table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string TableName = "monster";
|
public const string TableName = Metadata.TableName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the config directory path expected by the generated registration helper.
|
/// Gets the config directory path expected by the generated registration helper.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string ConfigRelativePath = "monster";
|
public const string ConfigRelativePath = Metadata.ConfigRelativePath;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the schema file path expected by the generated registration helper.
|
/// Gets the schema file path expected by the generated registration helper.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SchemaRelativePath = "schemas/monster.schema.json";
|
public const string SchemaRelativePath = Metadata.SchemaRelativePath;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers the generated config table using the schema-derived runtime conventions.
|
/// Registers the generated config table using the schema-derived runtime conventions.
|
||||||
@ -40,9 +71,9 @@ public static class MonsterConfigBindings
|
|||||||
}
|
}
|
||||||
|
|
||||||
return loader.RegisterTable<int, MonsterConfig>(
|
return loader.RegisterTable<int, MonsterConfig>(
|
||||||
TableName,
|
Metadata.TableName,
|
||||||
ConfigRelativePath,
|
Metadata.ConfigRelativePath,
|
||||||
SchemaRelativePath,
|
Metadata.SchemaRelativePath,
|
||||||
static config => config.Id,
|
static config => config.Id,
|
||||||
comparer);
|
comparer);
|
||||||
}
|
}
|
||||||
@ -60,7 +91,7 @@ public static class MonsterConfigBindings
|
|||||||
throw new global::System.ArgumentNullException(nameof(registry));
|
throw new global::System.ArgumentNullException(nameof(registry));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MonsterTable(registry.GetTable<int, MonsterConfig>(TableName));
|
return new MonsterTable(registry.GetTable<int, MonsterConfig>(Metadata.TableName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -77,7 +108,7 @@ public static class MonsterConfigBindings
|
|||||||
throw new global::System.ArgumentNullException(nameof(registry));
|
throw new global::System.ArgumentNullException(nameof(registry));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registry.TryGetTable<int, MonsterConfig>(TableName, out var innerTable) && innerTable is not null)
|
if (registry.TryGetTable<int, MonsterConfig>(Metadata.TableName, out var innerTable) && innerTable is not null)
|
||||||
{
|
{
|
||||||
table = new MonsterTable(innerTable);
|
table = new MonsterTable(innerTable);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -650,22 +650,58 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
|||||||
builder.AppendLine($"public static class {bindingsClassName}");
|
builder.AppendLine($"public static class {bindingsClassName}");
|
||||||
builder.AppendLine("{");
|
builder.AppendLine("{");
|
||||||
builder.AppendLine(" /// <summary>");
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
" /// Groups the schema-derived metadata constants so consumer code can reuse one stable entry point.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(" public static class Metadata");
|
||||||
|
builder.AppendLine(" {");
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
" /// Gets the logical config domain derived from the schema base name. The current runtime convention keeps this value aligned with the generated table name.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
$" public const string ConfigDomain = {SymbolDisplay.FormatLiteral(schema.TableRegistrationName, true)};");
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(" /// Gets the runtime registration name of the generated config table.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
$" public const string TableName = {SymbolDisplay.FormatLiteral(schema.TableRegistrationName, true)};");
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
" /// Gets the config directory path expected by the generated registration helper.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
$" public const string ConfigRelativePath = {SymbolDisplay.FormatLiteral(schema.ConfigRelativePath, true)};");
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(" /// Gets the schema file path expected by the generated registration helper.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
$" public const string SchemaRelativePath = {SymbolDisplay.FormatLiteral(schema.SchemaRelativePath, true)};");
|
||||||
|
builder.AppendLine(" }");
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
|
builder.AppendLine(
|
||||||
|
" /// Gets the logical config domain derived from the schema base name. The current runtime convention keeps this value aligned with the generated table name.");
|
||||||
|
builder.AppendLine(" /// </summary>");
|
||||||
|
builder.AppendLine(" public const string ConfigDomain = Metadata.ConfigDomain;");
|
||||||
|
builder.AppendLine();
|
||||||
|
builder.AppendLine(" /// <summary>");
|
||||||
builder.AppendLine(" /// Gets the runtime registration name of the generated config table.");
|
builder.AppendLine(" /// Gets the runtime registration name of the generated config table.");
|
||||||
builder.AppendLine(" /// </summary>");
|
builder.AppendLine(" /// </summary>");
|
||||||
builder.AppendLine(
|
builder.AppendLine(" public const string TableName = Metadata.TableName;");
|
||||||
$" public const string TableName = {SymbolDisplay.FormatLiteral(schema.TableRegistrationName, true)};");
|
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(" /// <summary>");
|
builder.AppendLine(" /// <summary>");
|
||||||
builder.AppendLine(" /// Gets the config directory path expected by the generated registration helper.");
|
builder.AppendLine(" /// Gets the config directory path expected by the generated registration helper.");
|
||||||
builder.AppendLine(" /// </summary>");
|
builder.AppendLine(" /// </summary>");
|
||||||
builder.AppendLine(
|
builder.AppendLine(" public const string ConfigRelativePath = Metadata.ConfigRelativePath;");
|
||||||
$" public const string ConfigRelativePath = {SymbolDisplay.FormatLiteral(schema.ConfigRelativePath, true)};");
|
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(" /// <summary>");
|
builder.AppendLine(" /// <summary>");
|
||||||
builder.AppendLine(" /// Gets the schema file path expected by the generated registration helper.");
|
builder.AppendLine(" /// Gets the schema file path expected by the generated registration helper.");
|
||||||
builder.AppendLine(" /// </summary>");
|
builder.AppendLine(" /// </summary>");
|
||||||
builder.AppendLine(
|
builder.AppendLine(" public const string SchemaRelativePath = Metadata.SchemaRelativePath;");
|
||||||
$" public const string SchemaRelativePath = {SymbolDisplay.FormatLiteral(schema.SchemaRelativePath, true)};");
|
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(" /// <summary>");
|
builder.AppendLine(" /// <summary>");
|
||||||
builder.AppendLine(
|
builder.AppendLine(
|
||||||
@ -688,9 +724,9 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
|||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(
|
builder.AppendLine(
|
||||||
$" return loader.RegisterTable<{schema.KeyClrType}, {schema.ClassName}>(");
|
$" return loader.RegisterTable<{schema.KeyClrType}, {schema.ClassName}>(");
|
||||||
builder.AppendLine(" TableName,");
|
builder.AppendLine(" Metadata.TableName,");
|
||||||
builder.AppendLine(" ConfigRelativePath,");
|
builder.AppendLine(" Metadata.ConfigRelativePath,");
|
||||||
builder.AppendLine(" SchemaRelativePath,");
|
builder.AppendLine(" Metadata.SchemaRelativePath,");
|
||||||
builder.AppendLine($" static config => config.{schema.KeyPropertyName},");
|
builder.AppendLine($" static config => config.{schema.KeyPropertyName},");
|
||||||
builder.AppendLine(" comparer);");
|
builder.AppendLine(" comparer);");
|
||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
@ -711,7 +747,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
|||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(
|
builder.AppendLine(
|
||||||
$" return new {schema.TableName}(registry.GetTable<{schema.KeyClrType}, {schema.ClassName}>(TableName));");
|
$" return new {schema.TableName}(registry.GetTable<{schema.KeyClrType}, {schema.ClassName}>(Metadata.TableName));");
|
||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(" /// <summary>");
|
builder.AppendLine(" /// <summary>");
|
||||||
@ -733,7 +769,7 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
|||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
builder.AppendLine(
|
builder.AppendLine(
|
||||||
$" if (registry.TryGetTable<{schema.KeyClrType}, {schema.ClassName}>(TableName, out var innerTable) && innerTable is not null)");
|
$" if (registry.TryGetTable<{schema.KeyClrType}, {schema.ClassName}>(Metadata.TableName, out var innerTable) && innerTable is not null)");
|
||||||
builder.AppendLine(" {");
|
builder.AppendLine(" {");
|
||||||
builder.AppendLine($" table = new {schema.TableName}(innerTable);");
|
builder.AppendLine($" table = new {schema.TableName}(innerTable);");
|
||||||
builder.AppendLine(" return true;");
|
builder.AppendLine(" return true;");
|
||||||
|
|||||||
@ -103,11 +103,21 @@ var slime = monsterTable.Get(1);
|
|||||||
|
|
||||||
这组辅助会把以下约定固化到生成代码里:
|
这组辅助会把以下约定固化到生成代码里:
|
||||||
|
|
||||||
|
- 配置域常量,例如 `MonsterConfigBindings.ConfigDomain`
|
||||||
- 表注册名,例如 `monster`
|
- 表注册名,例如 `monster`
|
||||||
- 配置目录相对路径,例如 `monster`
|
- 配置目录相对路径,例如 `monster`
|
||||||
- schema 相对路径,例如 `schemas/monster.schema.json`
|
- schema 相对路径,例如 `schemas/monster.schema.json`
|
||||||
- 主键提取逻辑,例如 `config => config.Id`
|
- 主键提取逻辑,例如 `config => config.Id`
|
||||||
|
|
||||||
|
如果你希望把这些约定作为一个统一入口传递或复用,也可以优先读取 `MonsterConfigBindings.Metadata` 下的常量:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var domain = MonsterConfigBindings.Metadata.ConfigDomain;
|
||||||
|
var tableName = MonsterConfigBindings.Metadata.TableName;
|
||||||
|
var configPath = MonsterConfigBindings.Metadata.ConfigRelativePath;
|
||||||
|
var schemaPath = MonsterConfigBindings.Metadata.SchemaRelativePath;
|
||||||
|
```
|
||||||
|
|
||||||
如果你需要自定义目录、表名或 key selector,仍然可以直接调用 `YamlConfigLoader.RegisterTable(...)` 原始重载。
|
如果你需要自定义目录、表名或 key selector,仍然可以直接调用 `YamlConfigLoader.RegisterTable(...)` 原始重载。
|
||||||
|
|
||||||
## 运行时校验行为
|
## 运行时校验行为
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user