mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
docs(game): 添加游戏内容配置系统文档和生成器功能
- 添加了 AI-First 配置系统完整文档,涵盖 YAML 配置、JSON Schema 结构、目录组织 - 实现了 Source Generator 自动生成配置类型、表包装、单表注册和访问辅助代码 - 提供了项目级聚合注册目录和配置浏览、校验、表单编辑等工具支持 - 集成了运行时只读查询、热重载、跨表引用校验等核心功能 - 添加了 VS Code 插件支持配置浏览、raw 编辑、schema 打开和递归校验功能
This commit is contained in:
parent
83c0c57f10
commit
4f966f9f50
@ -100,7 +100,9 @@ public class GeneratedConfigConsumerIntegrationTests
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(GeneratedConfigCatalog.Tables.Count, Is.EqualTo(1));
|
||||
Assert.That(
|
||||
GeneratedConfigCatalog.Tables.Select(static metadata => metadata.TableName),
|
||||
Does.Contain("monster"));
|
||||
Assert.That(GeneratedConfigCatalog.TryGetByTableName("monster", out var catalogEntry), Is.True);
|
||||
Assert.That(catalogEntry.ConfigDomain, Is.EqualTo("monster"));
|
||||
Assert.That(catalogEntry.ConfigRelativePath, Is.EqualTo("monster"));
|
||||
|
||||
@ -450,9 +450,14 @@ public class SchemaConfigGeneratorTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(catalogSource, Does.Contain("public static class GeneratedConfigCatalog"));
|
||||
Assert.That(catalogSource, Does.Contain("public sealed class GeneratedConfigRegistrationOptions"));
|
||||
Assert.That(catalogSource, Does.Contain("public static class GeneratedConfigRegistrationExtensions"));
|
||||
Assert.That(catalogSource, Does.Contain("loader.RegisterItemTable();"));
|
||||
Assert.That(catalogSource, Does.Contain("loader.RegisterMonsterTable();"));
|
||||
Assert.That(catalogSource, Does.Contain("public global::System.Collections.Generic.IEqualityComparer<string>? ItemComparer { get; init; }"));
|
||||
Assert.That(catalogSource, Does.Contain("public global::System.Collections.Generic.IEqualityComparer<int>? MonsterComparer { get; init; }"));
|
||||
Assert.That(catalogSource, Does.Contain("return RegisterAllGeneratedConfigTables(loader, options: null);"));
|
||||
Assert.That(catalogSource, Does.Contain("GeneratedConfigRegistrationOptions? options"));
|
||||
Assert.That(catalogSource, Does.Contain("loader.RegisterItemTable(options.ItemComparer);"));
|
||||
Assert.That(catalogSource, Does.Contain("loader.RegisterMonsterTable(options.MonsterComparer);"));
|
||||
Assert.That(catalogSource, Does.Contain("ItemConfigBindings.Metadata.TableName"));
|
||||
Assert.That(catalogSource, Does.Contain("MonsterConfigBindings.Metadata.TableName"));
|
||||
Assert.That(catalogSource, Does.Contain("public static bool TryGetByTableName(string tableName, out TableMetadata metadata)"));
|
||||
|
||||
@ -90,6 +90,17 @@ public static class GeneratedConfigCatalog
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Captures optional per-table registration overrides for the generated aggregate registration entry point.
|
||||
/// </summary>
|
||||
public sealed class GeneratedConfigRegistrationOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the optional key comparer forwarded to MonsterConfigBindings.RegisterMonsterTable(global::GFramework.Game.Config.YamlConfigLoader, global::System.Collections.Generic.IEqualityComparer<int>?) when aggregate registration runs.
|
||||
/// </summary>
|
||||
public global::System.Collections.Generic.IEqualityComparer<int>? MonsterComparer { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a single extension method that registers every generated config table discovered in the current consumer project.
|
||||
/// </summary>
|
||||
@ -109,7 +120,28 @@ public static class GeneratedConfigRegistrationExtensions
|
||||
throw new global::System.ArgumentNullException(nameof(loader));
|
||||
}
|
||||
|
||||
loader.RegisterMonsterTable();
|
||||
return RegisterAllGeneratedConfigTables(loader, options: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers all generated config tables while preserving optional per-table overrides such as custom key comparers.
|
||||
/// </summary>
|
||||
/// <param name="loader">Target YAML config loader.</param>
|
||||
/// <param name="options">Optional per-table overrides for aggregate registration; when null, all tables use their default comparer behavior.</param>
|
||||
/// <returns>The same loader instance after all generated table registrations have been applied.</returns>
|
||||
/// <exception cref="global::System.ArgumentNullException">When <paramref name="loader"/> is null.</exception>
|
||||
public static global::GFramework.Game.Config.YamlConfigLoader RegisterAllGeneratedConfigTables(
|
||||
this global::GFramework.Game.Config.YamlConfigLoader loader,
|
||||
GeneratedConfigRegistrationOptions? options)
|
||||
{
|
||||
if (loader is null)
|
||||
{
|
||||
throw new global::System.ArgumentNullException(nameof(loader));
|
||||
}
|
||||
|
||||
options ??= new GeneratedConfigRegistrationOptions();
|
||||
|
||||
loader.RegisterMonsterTable(options.MonsterComparer);
|
||||
return loader;
|
||||
}
|
||||
}
|
||||
@ -1082,6 +1082,31 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
builder.AppendLine(" metadata = default;");
|
||||
builder.AppendLine(" return false;");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("/// <summary>");
|
||||
builder.AppendLine(
|
||||
"/// Captures optional per-table registration overrides for the generated aggregate registration entry point.");
|
||||
builder.AppendLine("/// </summary>");
|
||||
builder.AppendLine("public sealed class GeneratedConfigRegistrationOptions");
|
||||
builder.AppendLine("{");
|
||||
|
||||
for (var index = 0; index < schemas.Count; index++)
|
||||
{
|
||||
var schema = schemas[index];
|
||||
builder.AppendLine(" /// <summary>");
|
||||
builder.AppendLine(
|
||||
$" /// Gets or sets the optional key comparer forwarded to {schema.EntityName}ConfigBindings.Register{schema.EntityName}Table(global::GFramework.Game.Config.YamlConfigLoader, global::System.Collections.Generic.IEqualityComparer<{schema.KeyClrType}>?) when aggregate registration runs.");
|
||||
builder.AppendLine(" /// </summary>");
|
||||
builder.AppendLine(
|
||||
$" public global::System.Collections.Generic.IEqualityComparer<{schema.KeyClrType}>? {schema.EntityName}Comparer {{ get; init; }}");
|
||||
|
||||
if (index < schemas.Count - 1)
|
||||
{
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("/// <summary>");
|
||||
@ -1107,10 +1132,35 @@ public sealed class SchemaConfigGenerator : IIncrementalGenerator
|
||||
builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(loader));");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" return RegisterAllGeneratedConfigTables(loader, options: null);");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" /// <summary>");
|
||||
builder.AppendLine(
|
||||
" /// Registers all generated config tables while preserving optional per-table overrides such as custom key comparers.");
|
||||
builder.AppendLine(" /// </summary>");
|
||||
builder.AppendLine(" /// <param name=\"loader\">Target YAML config loader.</param>");
|
||||
builder.AppendLine(
|
||||
" /// <param name=\"options\">Optional per-table overrides for aggregate registration; when null, all tables use their default comparer behavior.</param>");
|
||||
builder.AppendLine(" /// <returns>The same loader instance after all generated table registrations have been applied.</returns>");
|
||||
builder.AppendLine(
|
||||
" /// <exception cref=\"global::System.ArgumentNullException\">When <paramref name=\"loader\"/> is null.</exception>");
|
||||
builder.AppendLine(
|
||||
" public static global::GFramework.Game.Config.YamlConfigLoader RegisterAllGeneratedConfigTables(");
|
||||
builder.AppendLine(" this global::GFramework.Game.Config.YamlConfigLoader loader,");
|
||||
builder.AppendLine(" GeneratedConfigRegistrationOptions? options)");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" if (loader is null)");
|
||||
builder.AppendLine(" {");
|
||||
builder.AppendLine(" throw new global::System.ArgumentNullException(nameof(loader));");
|
||||
builder.AppendLine(" }");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" options ??= new GeneratedConfigRegistrationOptions();");
|
||||
builder.AppendLine();
|
||||
|
||||
foreach (var schema in schemas)
|
||||
{
|
||||
builder.AppendLine($" loader.Register{schema.EntityName}Table();");
|
||||
builder.AppendLine($" loader.Register{schema.EntityName}Table(options.{schema.EntityName}Comparer);");
|
||||
}
|
||||
|
||||
builder.AppendLine(" return loader;");
|
||||
|
||||
@ -454,6 +454,23 @@ if (GeneratedConfigCatalog.TryGetByTableName("monster", out var metadata))
|
||||
}
|
||||
```
|
||||
|
||||
如果你需要为某些表保留自定义 key comparer,也可以继续走聚合注册入口,而不是被迫退回逐表手写:
|
||||
|
||||
```csharp
|
||||
var loader = new YamlConfigLoader("config-root")
|
||||
.RegisterAllGeneratedConfigTables(
|
||||
new GeneratedConfigRegistrationOptions
|
||||
{
|
||||
ItemComparer = StringComparer.OrdinalIgnoreCase
|
||||
});
|
||||
```
|
||||
|
||||
这里的规则是:
|
||||
|
||||
- 未显式配置 comparer 的表,仍然使用各自 `Register{Entity}Table()` 的默认行为
|
||||
- 需要自定义 comparer 的表,可以通过 `GeneratedConfigRegistrationOptions` 按表覆盖
|
||||
- 如果项目希望继续完全手写某张表的注册流程,逐表 `Register*Table(...)` 入口仍然保留,作为兼容逃生通道
|
||||
|
||||
如果你需要自定义目录、表名或 key selector,仍然可以直接调用 `YamlConfigLoader.RegisterTable(...)` 原始重载。
|
||||
|
||||
如果你希望把 schema 路径、比较器以及未来扩展开关集中到一个对象里,推荐改用选项对象入口:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user