docs(sdk): 更新文档规范并添加VitePress配置

- 调整文档真实性原则,以源码和测试为首要证据源
- 新增模块README要求,规定所有用户包必须有说明文档
- 更新仓库文档规范,要求根README与文档站点分类一致
- 添加VitePress配置文件,支持中文搜索和泛型转义
- 创建入门指南文档,说明GFramework模块组成和接入路径
- 添加快速开始教程,演示Core模块最小使用示例
- 为Core模块添加详细README文档
- 为Core.Abstractions添加契约层说明文档
- 为Core.SourceGenerators添加源码生成器文档
- 为Game模块添加运行时层详细说明文档
This commit is contained in:
GeWuYou 2026-04-18 10:08:05 +08:00
parent b4459bf600
commit ebdc231c07
15 changed files with 1452 additions and 819 deletions

View File

@ -237,23 +237,32 @@ bash scripts/validate-csharp-naming.sh
- If a framework abstraction changes meaning or intended usage, update the explanatory comments in code as part of the
same change.
### Task Tracking
### Documentation Source Of Truth
- When working from a tracked implementation plan, contributors MUST update the corresponding tracking document under
`local-plan/todos/` in the same change.
- Tracking updates MUST reflect completed work, newly discovered issues, validation results, and the next recommended
recovery point.
- Completing code changes without updating the active tracking document is considered incomplete work.
- For any multi-step refactor, migration, or cross-module task, contributors MUST create or adopt a dedicated recovery
document under `local-plan/todos/` before making substantive code changes.
- Recovery documents MUST record the current phase, the active recovery point identifier, known risks, and the next
recommended resume step so another contributor or subagent can continue the work safely.
- Contributors MUST maintain a matching execution trace under `local-plan/traces/` for complex work. The trace should
record the current date, key decisions, validation milestones, and the immediate next step.
- When a task spans multiple commits or is likely to exceed a single agent context window, update both the recovery
document and the trace at each meaningful milestone before pausing or handing work off.
- If subagents are used on a complex task, the main agent MUST capture the delegated scope and any accepted findings in
the active recovery document or trace before continuing implementation.
- Treat source code, `*.csproj`, tests, generated snapshots, and packaging metadata as the primary evidence for
documentation updates.
- Treat `CoreGrid` as a secondary evidence source for real project adoption patterns, directory layouts, and end-to-end
usage examples.
- Treat existing `README.md` files and `docs/zh-CN/` pages as editable outputs, not authoritative truth.
- If existing documentation conflicts with code or tests, update the documentation to match the implementation instead
of preserving outdated wording.
- Do not publish example code, setup steps, or package guidance that cannot be traced back to code, tests, or a
verified consumer project.
### Module README Requirements
- Every user-facing package or module directory that contains a `*.csproj` intended for direct consumption MUST have a
sibling `README.md`.
- Use the canonical filename `README.md`. Do not introduce new `ReadMe.md` or other filename variants.
- A module README MUST describe:
- the module's purpose
- the relationship to adjacent runtime, abstractions, or generator packages
- the major subdirectories or subsystems the reader is expected to use
- the minimum adoption path
- the corresponding `docs/zh-CN/` entry points
- Adding a new top-level module directory without a `README.md` is considered incomplete work.
- If a module's responsibilities, setup, public API surface, generator inputs, or adoption path change, update that
module's `README.md` in the same change.
### Repository Documentation
@ -273,6 +282,32 @@ bash scripts/validate-csharp-naming.sh
documentation is considered incomplete work.
- Do not rely on “the code is self-explanatory” for framework features that consumers need to adopt; write the
adoption path down so future users do not need to rediscover it from source.
- The repository root `README.md` MUST mirror the current top-level documentation taxonomy used by the docs site.
Do not maintain a second, differently named navigation system in the root README.
- Prefer linking the root `README.md` to section landing pages such as `index.md` instead of deep-linking to a single
article when the target is intended to be a documentation category.
- If a docs category appears in VitePress navigation or sidebar, it MUST have a real landing page or be removed from
navigation in the same change.
- When examples are rewritten, preserve only the parts that remain true. Delete or replace speculative examples instead
of lightly editing them into another inaccurate form.
### Task Tracking
- When working from a tracked implementation plan, contributors MUST update the corresponding tracking document under
`local-plan/todos/` in the same change.
- Tracking updates MUST reflect completed work, newly discovered issues, validation results, and the next recommended
recovery point.
- Completing code changes without updating the active tracking document is considered incomplete work.
- For any multi-step refactor, migration, or cross-module task, contributors MUST create or adopt a dedicated recovery
document under `local-plan/todos/` before making substantive code changes.
- Recovery documents MUST record the current phase, the active recovery point identifier, known risks, and the next
recommended resume step so another contributor or subagent can continue the work safely.
- Contributors MUST maintain a matching execution trace under `local-plan/traces/` for complex work. The trace should
record the current date, key decisions, validation milestones, and the immediate next step.
- When a task spans multiple commits or is likely to exceed a single agent context window, update both the recovery
document and the trace at each meaningful milestone before pausing or handing work off.
- If subagents are used on a complex task, the main agent MUST capture the delegated scope and any accepted findings in
the active recovery document or trace before continuing implementation.
### Documentation Preview

View File

@ -1,27 +1,51 @@
# GFramework.Core.Abstractions
GFramework 框架的抽象层定义模块,包含所有核心组件的接口定义
`GFramework.Core.Abstractions` 承载 `Core` 运行时对应的接口、枚举和值对象,用来定义跨模块协作边界
## 主要内容
## 什么时候单独依赖它
- 架构核心接口 (IArchitecture, IArchitectureContext等)
- 数据模型接口 (IModel)
- 业务系统接口 (ISystem)
- 控制器接口 (IController)
- 命令与查询接口 (ICommand, IQuery)
- 事件系统接口 (IEvent, IEventBus)
- 依赖注入容器接口 (IIocContainer)
- 可绑定属性接口 (IBindableProperty)
- 状态管理接口 (IStore, IReducer, IStateSelector, IStoreBuilder)
- 日志系统接口 (ILogger)
- 你在做插件、适配层或扩展包,只想依赖契约,不想把完整运行时拉进来
- 你需要为测试、编辑器工具或生成器提供替身实现
- 你在做多模块拆分,希望上层只面向接口编程
## 设计原则
如果你只是直接使用框架功能,优先安装 `GFramework.Core`
- 接口隔离,每个接口职责单一
- 依赖倒置,上层依赖抽象接口
- 类型安全,充分利用泛型系统
- 广泛兼容,基于 netstandard2.0
## 包关系
## 详细文档
- 契约层:`GFramework.Core.Abstractions`
- 实现层:`GFramework.Core`
- 相关扩展:
- `GFramework.Cqrs.Abstractions`
- `GFramework.Game.Abstractions`
参见 [docs/zh-CN/abstractions/](../docs/zh-CN/abstractions/) 目录下的详细文档。
## 契约地图
| 目录 | 作用 |
| --- | --- |
| `Architectures/` | `IArchitecture`、模块、阶段监听与服务管理契约 |
| `Command/` / `Query/` | 旧版命令与查询执行器接口 |
| `Controller/` | `IController` |
| `Events/` | 事件契约、解绑接口与传播上下文 |
| `Model/` / `Systems/` / `Utility/` | 核心组件接口 |
| `State/` / `StateManagement/` | 状态机、Store、reducer、selector 契约 |
| `Property/` | `IBindableProperty` 与只读属性接口 |
| `Resource/` | 资源管理与释放策略契约 |
| `Localization/` | 本地化表、格式化与异常类型 |
| `Logging/` | logger、log entry、factory 相关契约 |
| `Ioc/` | `IIocContainer` |
| `Lifecycle/` | 初始化 / 销毁生命周期契约 |
| `Coroutine/` | 时间源、yield 指令与协程状态枚举 |
| `Pause/` | 暂停栈、token 与状态事件 |
| `Storage/` / `Serializer/` / `Versioning/` | 通用存储、序列化与版本化契约 |
## 采用建议
- 框架消费者通常同时安装 `GFramework.Core``GFramework.Core.Abstractions`
- 若你只需要对接口编程,可以仅引用本包,再在应用层自行提供实现
- 若你在写上层模块,优先把公共契约放在 `*.Abstractions`,实现放在对应 runtime 包
## 对应文档
- 抽象接口栏目:[`../docs/zh-CN/abstractions/index.md`](../docs/zh-CN/abstractions/index.md)
- Core 抽象页:[`../docs/zh-CN/abstractions/core-abstractions.md`](../docs/zh-CN/abstractions/core-abstractions.md)
- Core 运行时入口:[`../GFramework.Core/README.md`](../GFramework.Core/README.md)

View File

@ -1,63 +1,95 @@
# GFramework.SourceGenerators
# GFramework.Core.SourceGenerators
Core 侧通用源码生成器模块
`GFramework.Core.SourceGenerators` 承载 Core 侧的通用源码生成器与分析器,用来减少样板代码并把部分约束前移到编译期
## Context Get 注入
## 模块定位
当类本身是上下文感知类型时,可以通过字段特性生成一个手动调用的注入方法:
这个包属于编译期工具链,不是运行时库。
- `[GetService]`
- `[GetServices]`
- `[GetSystem]`
- `[GetSystems]`
当前仓库中的主要目录:
- `Architectures/`
- `Analyzers/`
- `Bases/`
- `Enums/`
- `Logging/`
- `Rule/`
- `Diagnostics/`
对应的生成器家族主要包括:
- 日志相关生成器
- `ContextAware` 及上下文注入辅助
- 枚举扩展生成器
- 优先级相关生成器
- 模块自动注册
- 注册可见性分析器
## 包关系
- 运行时:`GFramework.Core`
- 契约与特性:`GFramework.Core.SourceGenerators.Abstractions`
- 公共生成器支撑:`GFramework.SourceGenerators.Common`
如果你还需要游戏配置 schema 生成或 Godot 专用生成器,应分别安装:
- `GFramework.Game.SourceGenerators`
- `GFramework.Godot.SourceGenerators`
## 主要能力
### 上下文注入与 `ContextAware`
该包支持围绕上下文感知类型生成辅助代码,例如:
- `[ContextAware]`
- `[GetModel]`
- `[GetModels]`
- `[GetSystem]`
- `[GetSystems]`
- `[GetUtility]`
- `[GetUtilities]`
- `[GetService]`
- `[GetServices]`
- `[GetAll]`
上下文感知类满足以下任一条件即可:
这类生成器适合用于 View、Controller、Godot 节点包装或其他需要频繁访问架构上下文的类型。
- 类上带有 `[ContextAware]`
- 继承 `ContextAwareBase`
- 实现 `IContextAware`
### 日志辅助
生成器会生成 `__InjectContextBindings_Generated()`,需要在合适的生命周期中手动调用。在 Godot 中通常放在 `_Ready()`
支持通过生成器减少 `ILogger` 相关样板代码。
```csharp
using GFramework.SourceGenerators.Abstractions.Rule;
### 注册分析器
[ContextAware]
public partial class InventoryPanel
{
[GetModel]
private IInventoryModel _inventory = null!;
包内还包含分析器,用来检查 `Model``System``Utility` 的使用点是否能在所属架构中找到静态可见注册,帮助尽早发现“代码可以编译、运行时却缺注册”的问题。
[GetServices]
private IReadOnlyList<IInventoryStrategy> _strategies = null!;
## 最小接入路径
public override void _Ready()
{
__InjectContextBindings_Generated();
}
}
```xml
<ItemGroup>
<PackageReference Include="GeWuYou.GFramework.Core.SourceGenerators"
Version="x.y.z"
PrivateAssets="all"
ExcludeAssets="runtime" />
</ItemGroup>
```
`[GetAll]` 作用于类本身,会自动扫描字段并推断 `Model``System``Utility` 相关的 `GetX` 调用;已显式标记字段的优先级更高。
如果你想查看生成代码,可在消费者项目里启用:
`Service``Services` 绑定不会在 `[GetAll]` 下自动推断。对于普通引用类型字段,请显式使用 `[GetService]`
`[GetServices]`,避免将非上下文服务字段误判为服务依赖。
```xml
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
```
`[GetAll]` 会跳过 `const``static``readonly` 字段。若某个字段本来会被 `[GetAll]` 推断为
`Model``System``Utility` 绑定,但因为是不可赋值的 `static``readonly` 字段而被跳过,生成器会发出警告提示该字段不会参与生成。
## 适用场景
## 注册分析器
- 你希望减少上下文绑定与日志相关样板代码
- 你需要在编译期发现部分注册可见性问题
- 你在做模块化架构,希望固定某些重复注册模式
包现在同时包含一个注册可见性分析器,用于检查 `Model``System``Utility` 的使用点是否能在所属架构中找到静态可见注册。
## 对应文档
- 覆盖字段特性注入:`[GetModel]``[GetModels]``[GetSystem]``[GetSystems]``[GetUtility]``[GetUtilities]`
- 覆盖手写调用:`GetModel<T>()``GetModels<T>()``GetSystem<T>()``GetSystems<T>()``GetUtility<T>()``GetUtilities<T>()`
- 默认报告 `Warning`
- 当前只分析静态可见的注册路径,例如 `OnInitialize()``InstallModules()``InstallModule(new Module())`
对于反射、运行时条件分支、外部程序集动态注册等路径,分析器不会强行推断;当无法唯一确定组件所属架构时,也会选择不报,优先降低误报。
- 源码生成器总览:[`../docs/zh-CN/source-generators/index.md`](../docs/zh-CN/source-generators/index.md)
- Core 栏目:[`../docs/zh-CN/core/index.md`](../docs/zh-CN/core/index.md)

View File

@ -1,32 +1,84 @@
# GFramework.Core
GFramework 框架的核心模块提供MVC架构的基础设施
`GFramework.Core` 是框架的基础运行时,负责架构生命周期、组件注册、上下文访问,以及不依赖具体引擎的通用能力
## 主要功能
如果你只想先把框架跑起来,应先从这个模块开始。
- **Architecture** - 应用程序架构管理,支持依赖注入、生命周期管理和模块化扩展
- **Model** - 数据模型层,管理应用状态和数据
- **System** - 业务逻辑层,处理核心业务逻辑和事件响应
- **Controller** - 控制器层处理用户输入和UI协调
- **Command** - 命令模式实现,封装用户操作
- **Query** - 查询模式实现支持CQRS架构
- **Events** - 事件系统,实现组件间松耦合通信
- **IoC** - 轻量级依赖注入容器
- **Property** - 可绑定属性,支持数据绑定和响应式编程
- **StateManagement** - 集中式状态容器,支持状态归约、选择器和诊断
- **Utility** - 无状态工具类
- **Pool** - 对象池系统减少GC压力
- **Extensions** - 框架扩展方法
- **Logging** - 日志系统
- **Environment** - 环境配置管理
## 模块定位
## 设计原则
这一层提供:
- 与平台解耦,不依赖特定游戏引擎
- 接口隔离,职责单一
- 依赖倒置,面向接口编程
- 组合优于继承
- `Architecture``ArchitectureContext`
- `Model` / `System` / `Utility` 运行时
- 旧版 `Command` / `Query` 执行器
- 事件、属性、状态机、状态管理
- 资源、日志、协程、并发、环境与本地化
## 详细文档
它不负责:
参见 [docs/zh-CN/core/](../docs/zh-CN/core/) 目录下的详细文档。
- 游戏内容配置、Scene / UI / Storage 等游戏层能力
- Godot 节点与场景集成
- 新版 CQRS 请求模型的消息契约定义
## 包关系
- 直接依赖:
- `GFramework.Cqrs`
- `GFramework.Cqrs.Abstractions`
- `GFramework.Core.Abstractions`
- 常见上层模块:
- `GFramework.Game`
- `GFramework.Godot`
如果你只需要契约,不需要实现层,改为依赖 [`../GFramework.Core.Abstractions/README.md`](../GFramework.Core.Abstractions/README.md)。
## 子系统地图
| 目录 | 作用 |
| --- | --- |
| `Architectures/` | 架构入口、上下文、生命周期、模块安装与组件注册 |
| `Command/` | 旧版命令执行器与同步 / 异步命令基类 |
| `Query/` | 旧版查询执行器与同步 / 异步查询基类 |
| `Events/` | 事件总线、事件作用域、统计与过滤 |
| `Property/` | `BindableProperty<T>` 与相关解绑对象 |
| `State/` | 状态机与状态切换事件 |
| `StateManagement/` | Store、selector、middleware 与状态诊断 |
| `Coroutine/` | 协程调度、快照、统计与优先级 |
| `Resource/` | 资源缓存、句柄和释放策略 |
| `Logging/` | logger、factory、配置与组合日志器 |
| `Ioc/` | 基于 `Microsoft.Extensions.DependencyInjection` 的容器适配 |
| `Concurrency/` | 键控异步锁与统计 |
| `Pause/` | 暂停栈和暂停范围 |
| `Localization/` | 本地化表与格式化入口 |
| `Functional/` | `Option``Result` 等轻量函数式工具 |
| `Extensions/` | 上下文与集合等扩展方法 |
## 最小接入路径
```bash
dotnet add package GeWuYou.GFramework.Core
dotnet add package GeWuYou.GFramework.Core.Abstractions
```
最小入口:
1. 继承 `Architecture`
2. 在 `OnInitialize()` 中注册模型、系统、工具或模块
3. 通过 `architecture.Context``ContextAwareBase` 的扩展方法访问上下文
最小示例见:
- [`../docs/zh-CN/getting-started/quick-start.md`](../docs/zh-CN/getting-started/quick-start.md)
## 什么时候继续接别的包
- 需要推荐的新请求模型:加 `GFramework.Cqrs`
- 需要游戏层路由、设置、配置和存储:加 `GFramework.Game`
- 需要 Godot 节点与场景适配:加 `GFramework.Godot`
- 需要编译期生成日志、上下文注入或模块注册:加 `GFramework.Core.SourceGenerators`
## 对应文档
- Core 栏目:[`../docs/zh-CN/core/index.md`](../docs/zh-CN/core/index.md)
- CQRS[`../docs/zh-CN/core/cqrs.md`](../docs/zh-CN/core/cqrs.md)
- 入门指南:[`../docs/zh-CN/getting-started/index.md`](../docs/zh-CN/getting-started/index.md)

View File

@ -0,0 +1,101 @@
# GFramework.Cqrs.Abstractions
`GFramework.Cqrs.Abstractions` 提供 GFramework CQRS 的最小契约层。它只包含消息接口、处理器接口、运行时 seam 和管道契约,不包含默认 dispatcher、处理器扫描或任何 `GFramework.Core` 运行时实现。适合以下场景:
- 你的业务程序集只需要声明 Command、Query、Notification、Stream Request 或处理器接口。
- 你希望把消息契约放在更稳定的基础层,避免直接依赖默认 runtime 实现。
- 你要为其他运行时、测试环境或自定义容器实现兼容的 CQRS 接口。
## 模块定位
- 这是 CQRS 的“协议层”。
- 目标框架为 `netstandard2.1`,用于被更上层模块稳定引用。
- 当前包不负责处理器自动注册,也不负责请求分发。
如果你需要默认消息基类、处理器基类、上下文扩展方法和运行时实现,请使用 `GeWuYou.GFramework.Cqrs`
## 包关系
推荐按职责引用:
- `GeWuYou.GFramework.Cqrs.Abstractions`
- 提供 `IRequest<TResponse>``INotification``IStreamRequest<TResponse>``IRequestHandler<,>``INotificationHandler<>``IPipelineBehavior<,>``ICqrsRuntime``ICqrsContext``Unit` 等基础契约。
- `GeWuYou.GFramework.Cqrs`
- 引用本包,并提供默认 runtime、处理器注册、消息基类、处理器基类、上下文扩展方法。
- `GeWuYou.GFramework.Cqrs.SourceGenerators`
- 可选。面向消费端程序集生成 `ICqrsHandlerRegistry` 注册表,减少冷启动反射扫描;未生成或不适用时,运行时仍会回退到反射注册。
## 子系统地图
本包当前可以分为几类契约:
- 消息契约
- `Cqrs/IRequest.cs`
- `Cqrs/INotification.cs`
- `Cqrs/IStreamRequest.cs`
- `Cqrs/Command/ICommand.cs`
- `Cqrs/Query/IQuery.cs`
- `Cqrs/Request/IRequestInput.cs`
- `Cqrs/Command/ICommandInput.cs`
- `Cqrs/Query/IQueryInput.cs`
- `Cqrs/Notification/INotificationInput.cs`
- 处理器契约
- `Cqrs/IRequestHandler.cs`
- `Cqrs/INotificationHandler.cs`
- `Cqrs/IStreamRequestHandler.cs`
- 运行时 seam
- `Cqrs/ICqrsRuntime.cs`
- `Cqrs/ICqrsContext.cs`
- `Cqrs/ICqrsHandlerRegistrar.cs`
- 管道与辅助类型
- `Cqrs/IPipelineBehavior.cs`
- `Cqrs/MessageHandlerDelegate.cs`
- `Cqrs/Unit.cs`
## 最小接入路径
如果你只想在基础层定义一个可被上层 runtime 消费的 Query可以只依赖本包
```bash
dotnet add package GeWuYou.GFramework.Cqrs.Abstractions
```
```csharp
using GFramework.Cqrs.Abstractions.Cqrs.Query;
public sealed record GetPlayerProfileInput(int PlayerId) : IQueryInput;
public sealed class GetPlayerProfileQuery : IQuery<PlayerProfileDto>
{
public GetPlayerProfileQuery(GetPlayerProfileInput input)
{
Input = input;
}
public GetPlayerProfileInput Input { get; }
}
public sealed class GetPlayerProfileHandler
: IRequestHandler<GetPlayerProfileQuery, PlayerProfileDto>
{
public ValueTask<PlayerProfileDto> Handle(
GetPlayerProfileQuery request,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
```
这条路径适合“只声明契约”的场景。真正执行分发时,仍需要上层提供 `ICqrsRuntime` 和处理器注册流程,通常由 `GeWuYou.GFramework.Cqrs``GFramework.Core` 完成。
## 使用边界
- 只引用本包时,没有 `CommandBase<TInput, TResponse>``QueryBase<TInput, TResponse>``NotificationBase<TInput>` 等消息基类。
- 只引用本包时,没有 `AbstractCommandHandler``AbstractQueryHandler``AbstractNotificationHandler` 等处理器基类。
- `ICqrsContext` 当前是轻量 marker seam默认 runtime 在需要向 `IContextAware` 处理器注入上下文时,仍要求传入的上下文同时实现 `IArchitectureContext`
## 文档入口
- 运行时与整体接入说明:`docs/zh-CN/core/cqrs.md`
- 如果你需要默认实现而不是契约层,请看 `GFramework.Cqrs/README.md`

View File

@ -0,0 +1,67 @@
# GFramework.Cqrs.SourceGenerators
`GFramework.Cqrs.SourceGenerators` 用于在编译期为当前业务程序集生成 CQRS handler registry减少运行时程序集扫描与反射注册成本。
## 模块定位
这个包是编译期生成器,不是运行时消息或处理器库。
生成器会分析当前业务程序集中的:
- `IRequestHandler<,>`
- `INotificationHandler<>`
- `IStreamRequestHandler<,>`
并生成:
- `ICqrsHandlerRegistry` 实现
- 程序集级 `CqrsHandlerRegistryAttribute`
- 必要时的 `CqrsReflectionFallbackAttribute` 元数据
## 包关系
- 运行时:`GFramework.Cqrs`
- 契约层:`GFramework.Cqrs.Abstractions`
- 生成器:`GFramework.Cqrs.SourceGenerators`
不安装这个包也可以正常使用 CQRS区别只在于运行时会更多依赖反射扫描注册 handlers。
## 当前代码入口
仓库内该包的主要实现位于:
- `Cqrs/CqrsHandlerRegistryGenerator.cs`
它会在可以安全生成静态注册器时前移注册工作;对无法由生成代码直接引用的 handler则通过 reflection fallback 元数据让运行时做定向补扫,而不是整程序集盲扫。
## 最小接入路径
```xml
<ItemGroup>
<PackageReference Include="GeWuYou.GFramework.Cqrs" Version="x.y.z" />
<PackageReference Include="GeWuYou.GFramework.Cqrs.Abstractions" Version="x.y.z" />
<PackageReference Include="GeWuYou.GFramework.Cqrs.SourceGenerators"
Version="x.y.z"
PrivateAssets="all"
ExcludeAssets="runtime" />
</ItemGroup>
```
运行时侧仍按正常方式注册程序集:
```csharp
RegisterCqrsHandlersFromAssembly(typeof(GameArchitecture).Assembly);
```
安装生成器后,运行时会优先走生成的 registry无法静态表达的部分再走定向回退。
## 什么时候值得安装
- 你的业务程序集里 handler 数量较多
- 你希望缩小冷启动时的反射扫描范围
- 你需要把 handler 注册路径收束到编译期并保持可诊断
## 对应文档
- CQRS 栏目:[`../docs/zh-CN/core/cqrs.md`](../docs/zh-CN/core/cqrs.md)
- 源码生成器总览:[`../docs/zh-CN/source-generators/index.md`](../docs/zh-CN/source-generators/index.md)

154
GFramework.Cqrs/README.md Normal file
View File

@ -0,0 +1,154 @@
# GFramework.Cqrs
`GFramework.Cqrs` 是 GFramework 的默认 CQRS runtime 包。它在 `GFramework.Cqrs.Abstractions` 之上提供请求分发、通知发布、流式请求处理、处理器注册、上下文扩展方法,以及消息/处理器基类,面向直接使用 GFramework CQRS 的业务模块。
## 模块定位
- 这是 CQRS 的“默认实现层”。
- 包内同时承载运行时基础设施和面向业务代码的便捷基类。
- 它依赖 `GFramework.Cqrs.Abstractions``GFramework.Core.Abstractions`
- 在标准 GFramework 架构启动路径中,`GFramework.Core``CqrsRuntimeModule` 会把默认 runtime、处理器注册器与注册服务自动接入容器。
如果你只需要声明跨模块共享的消息契约,而不想依赖默认 runtime请改为引用 `GeWuYou.GFramework.Cqrs.Abstractions`
## 包关系
推荐的依赖关系如下:
- `GeWuYou.GFramework.Cqrs.Abstractions`
- 最小 CQRS 契约层。
- `GeWuYou.GFramework.Cqrs`
- 默认 runtime 与业务侧常用基类。
- `GeWuYou.GFramework.Cqrs.SourceGenerators`
- 可选。为消费端程序集生成 `ICqrsHandlerRegistry`,运行时优先走生成注册表;缺失或不适用时,回退到反射扫描。
- `GFramework.Core`
- 架构上下文中实际调用 `ICqrsRuntime`,并在模块初始化时注册 CQRS 基础设施。
## 子系统地图
本包当前主要由以下子系统组成:
- 消息基类
- `Command/CommandBase.cs`
- `Query/QueryBase.cs`
- `Request/RequestBase.cs`
- `Notification/NotificationBase.cs`
- 处理器基类
- `Cqrs/CqrsContextAwareHandlerBase.cs`
- `Cqrs/Command/AbstractCommandHandler.cs`
- `Cqrs/Query/AbstractQueryHandler.cs`
- `Cqrs/Notification/AbstractNotificationHandler.cs`
- `Cqrs/Request/AbstractRequestHandler.cs`
- `Cqrs/Request/AbstractStreamRequestHandler.cs`
- `Cqrs/Command/AbstractStreamCommandHandler.cs`
- `Cqrs/Query/AbstractStreamQueryHandler.cs`
- 请求管道
- `Cqrs/Behaviors/LoggingBehavior.cs`
- `Cqrs/Behaviors/PerformanceBehavior.cs`
- 管道契约定义在 `GFramework.Cqrs.Abstractions``IPipelineBehavior<,>`
- 默认 runtime 与注册入口
- `CqrsRuntimeFactory.cs`
- `Internal/CqrsDispatcher.cs`
- `Internal/CqrsHandlerRegistrar.cs`
- `Internal/DefaultCqrsHandlerRegistrar.cs`
- `Internal/DefaultCqrsRegistrationService.cs`
- 生成注册表协作接口
- `ICqrsHandlerRegistry.cs`
- `CqrsHandlerRegistryAttribute.cs`
- `CqrsReflectionFallbackAttribute.cs`
- 业务侧扩展入口
- `Extensions/ContextAwareCqrsExtensions.cs`
- `Extensions/ContextAwareCqrsCommandExtensions.cs`
- `Extensions/ContextAwareCqrsQueryExtensions.cs`
## 最小接入路径
在标准 GFramework 架构中,最小接入通常是“安装包 + 定义消息 + 定义处理器 + 通过上下文发送”:
```bash
dotnet add package GeWuYou.GFramework.Cqrs
dotnet add package GeWuYou.GFramework.Cqrs.Abstractions
```
如果你希望减少处理器注册时的反射扫描,再额外安装:
```bash
dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
```
示例:
```csharp
using GFramework.Cqrs.Command;
using GFramework.Cqrs.Cqrs.Command;
using GFramework.Cqrs.Abstractions.Cqrs.Command;
public sealed record CreatePlayerInput(string Name) : ICommandInput;
public sealed class CreatePlayerCommand : CommandBase<CreatePlayerInput, int>
{
public CreatePlayerCommand(CreatePlayerInput input) : base(input)
{
}
}
public sealed class CreatePlayerCommandHandler
: AbstractCommandHandler<CreatePlayerCommand, int>
{
public override ValueTask<int> Handle(
CreatePlayerCommand command,
CancellationToken cancellationToken)
{
var playerModel = Context.GetModel<PlayerModel>();
var playerId = playerModel.Create(command.Input.Name);
return ValueTask.FromResult(playerId);
}
}
```
`IContextAware` 对象内发送命令:
```csharp
using GFramework.Cqrs.Extensions;
var playerId = await this.SendAsync(new CreatePlayerCommand(new CreatePlayerInput("Alice")));
```
`ArchitectureContext` 上也可以直接使用统一 CQRS 入口,例如 `SendRequestAsync``SendQueryAsync``PublishAsync``CreateStream`
## 运行时行为
- 请求分发
- `CqrsDispatcher` 按请求实际类型解析 `IRequestHandler<,>`,未找到处理器会抛出异常。
- 通知分发
- 通知会分发给所有已注册 `INotificationHandler<>`;零处理器时默认静默完成。
- 流式请求
- 通过 `IStreamRequest<TResponse>``IStreamRequestHandler<,>` 返回 `IAsyncEnumerable<TResponse>`
- 上下文注入
- 处理器基类继承 `CqrsContextAwareHandlerBase`runtime 会在分发前注入当前 `IArchitectureContext`
- 如果处理器或行为需要上下文注入,而当前 `ICqrsContext` 不是 `IArchitectureContext`,默认实现会抛出异常。
- 管道行为
- 所有已注册 `IPipelineBehavior<TRequest, TResponse>` 会包裹请求处理器执行。
- 当前包内提供了 `LoggingBehavior``PerformanceBehavior` 两个可复用行为。
## 处理器注册与程序集接入
默认注册流程由 `ICqrsRegistrationService.RegisterHandlers(IEnumerable<Assembly>)` 协调,语义是:
- 同一程序集按稳定键去重,避免重复注册。
- 优先尝试消费端程序集上的 `ICqrsHandlerRegistry` 生成注册器。
- 生成注册器不可用,或声明了 `CqrsReflectionFallbackAttribute` 时,回退到反射扫描。
- 处理器以 transient 方式注册,避免上下文感知处理器在并发请求间共享可变上下文。
如果你走标准 `GFramework.Core` 架构初始化路径,这些步骤通常由框架自动完成;裸容器或测试环境则需要显式补齐 runtime 与注册入口。
## 适用边界
- 这个包是默认实现,不是“纯契约包”。
- 处理器基类依赖 runtime 在分发前注入上下文,不适合脱离 dispatcher 直接手动实例化后调用。
- README 中的消息基类和 handler 基类位于 `GFramework.Cqrs`,接口契约位于 `GFramework.Cqrs.Abstractions`;最小示例通常需要同时引入这两个命名空间层级。
## 文档入口
- 总体文档:`docs/zh-CN/core/cqrs.md`
- 契约层说明:`GFramework.Cqrs.Abstractions/README.md`

View File

@ -1,13 +1,240 @@
# GFramework.Game.Abstractions
GFramework.Game 模块的抽象层,提供游戏业务相关的接口定义
`GFramework.Game.Abstractions``GFramework` 游戏层的契约包
## 主要内容
它建立在 `GFramework.Core.Abstractions` 之上,只定义接口、枚举、路由上下文、事件契约和少量可直接复用的数据类型,不提供完整运行时实现。它的主要用途是把游戏业务层、引擎适配层、公共 feature 包与具体实现解耦。
- 游戏业务常用抽象接口
- 与 GFramework.Core.Abstractions 配合使用的契约
如果你需要可直接运行的默认实现,请改为依赖 `GFramework.Game`
## 使用建议
## 包定位
- 若需直接使用完整游戏扩展能力,优先使用 GFramework.Game
- 若在做模块拆分或需要解耦,可单独依赖本包
- 为游戏相关子系统提供稳定契约,而不是默认实现。
- 适合被多个程序集共同引用,避免公共业务层直接依赖具体运行时。
- 典型使用场景:
- 定义 `IScene``IUiPage``ISettingsData``IData` 等业务对象
- 让 feature 包只感知 `IConfigRegistry``ISaveRepository<T>``ISettingsModel``IUiRouter``ISceneRouter`
- 在引擎适配层之外共享设置、场景参数、UI 参数、存档数据类型
## 与相邻包的关系
- `GFramework.Core.Abstractions`
- 本包直接依赖它。
- 提供 `ISystem``IModel``IUtility`、上下文 utility 等底层抽象。
- `GFramework.Game`
- 本包的主要实现层。
- `FileStorage``ScopedStorage``JsonSerializer``SettingsModel<TRepository>``SaveRepository<TSaveData>``SceneRouterBase``UiRouterBase``YamlConfigLoader` 等都在实现这里的契约。
- 引擎适配包或项目代码
- `IUiFactory``ISceneFactory``IUiRoot``ISceneRoot`、资源注册表等通常由引擎适配层或游戏项目自己实现。
- CoreGrid 的真实结构也是这样:页面/场景 factory、root、registry 在项目层,运行时基类和契约来自 `GFramework.Game` 与本包。
## 子系统地图
### `Config/`
静态内容配置的读取侧契约。
- `IConfigLoader`
- `IConfigRegistry`
- `IConfigTable`
- `ConfigLoadException`
- `ConfigLoadDiagnostic`
- `ConfigLoadFailureKind`
这一层只描述“如何注册与访问配置表”,不关心底层来自 YAML、二进制还是远程源。
### `Data/`
可写数据与存档契约。
- `IData`
- `IVersionedData`
- `IDataLocation`
- `IDataLocationProvider`
- `IDataRepository`
- `ISettingsDataRepository`
- `ISaveRepository<TSaveData>`
- `ISaveMigration<TSaveData>`
- `DataRepositoryOptions`
- `Data/Events/*`
这一层让业务代码不需要知道数据最终按“单文件一项”还是“统一文件多 section”持久化。
### `Setting/`
设置系统契约。
- `ISettingsData`
- `IResetApplyAbleSettings`
- `ISettingsModel`
- `ISettingsSystem`
- `ISettingsMigration`
- `ISettingsChangedEvent`
- `Setting/Data/*`
- 内置了 `AudioSettings``GraphicsSettings``LocalizationSettings` 三类常见设置数据
这里定义的是“设置生命周期和应用语义”,不限定具体引擎。
### `Scene/`
场景导航契约。
- `IScene`
- `ISceneBehavior`
- `ISceneFactory`
- `ISceneRoot`
- `ISceneRouter`
- `ISceneTransitionHandler`
- `ISceneAroundTransitionHandler`
- `ISceneRouteGuard`
- `IGameSceneRegistry<T>`
如果你想把场景定义放在公共业务层,通常依赖本包就够了。
### `UI/`
UI 页面与路由契约。
- `IUiPage`
- `IUiPageBehavior`
- `IUiFactory`
- `IUiRoot`
- `IUiRouter`
- `IUiTransitionHandler`
- `IUiAroundTransitionHandler`
- `IUiRouteGuard`
- `UiHandle`
- `UiTransitionHandlerOptions`
- `UiInteractionProfile`
`IUiRouter` 不只覆盖页面栈,还覆盖 Overlay / Modal / Toast / Topmost 等层级 UI 语义。
### `Routing/`
- `IRoute`
- `IRouteContext`
- `IRouteGuard<TRoute>`
Scene 与 UI 路由共享这套基础约定。
### `Storage/`
- `IFileStorage`
- `IScopedStorage`
它们继承自核心层的 `IStorage`,用于表达“文件存储实现”和“带作用域前缀的存储实现”这两个角色。
### `Asset/``Enums/`
- `Asset/`
- 资源注册表契约,如 `IAssetRegistry<T>`
- `Enums/`
- UI/Scene 转场、UI 层级、输入动作、存储类型等公共枚举
## 最小接入路径
### 1. 只想在公共业务层声明游戏对象
直接依赖本包,定义业务数据和交互参数:
```csharp
using GFramework.Game.Abstractions.Data;
using GFramework.Game.Abstractions.Scene;
using GFramework.Game.Abstractions.UI;
public sealed class GameSaveData : IVersionedData
{
public int Version { get; set; } = 1;
public DateTime LastModified { get; set; } = DateTime.UtcNow;
}
public sealed class GameplayEnterParam : ISceneEnterParam
{
public required string Seed { get; init; }
}
public sealed class PauseMenuParam : IUiPageEnterParam
{
public bool AllowRestart { get; init; }
}
```
这个阶段不需要把 `GFramework.Game` 一起带进来。
### 2. 只想让 feature 包依赖抽象,不绑定具体实现
直接面向接口编程:
```csharp
public sealed class ContinueGameCommandHandler
{
private readonly ISaveRepository<GameSaveData> _saveRepository;
private readonly ISceneRouter _sceneRouter;
public ContinueGameCommandHandler(
ISaveRepository<GameSaveData> saveRepository,
ISceneRouter sceneRouter)
{
_saveRepository = saveRepository;
_sceneRouter = sceneRouter;
}
}
```
这样 feature 包不必知道底层到底是 `SaveRepository<TSaveData>`、Godot 适配层,还是你自己的实现。
### 3. 什么时候要升级到 `GFramework.Game`
一旦你需要下面任一项,就不该只停留在本包:
- 默认的 JSON 序列化实现
- 文件系统存储实现
- 设置模型与系统实现
- 槽位存档仓库实现
- YAML 配置加载器
- Scene / UI 路由基类
也就是说,本包回答的是“项目各层如何约定”,`GFramework.Game` 回答的是“这些约定默认怎么跑起来”。
## CoreGrid 里的真实用法线索
CoreGrid 对本包的使用方式,能比较清楚地说明它的职责边界:
- 公共脚本广泛引用:
- `IUiRouter`
- `ISceneRouter`
- `ISettingsModel`
- `ISettingsSystem`
- `ISaveRepository<GameSaveData>`
- `IConfigRegistry`
- 业务数据和参数实现引用:
- `IData` / `IVersionedData`
- `ISceneEnterParam`
- `IUiPageEnterParam`
- 真正的实现和装配则放在:
- `GFramework.Game`
- `GFramework.Godot.*`
- CoreGrid 自己的模块、factory、root、registry
这正是本包的设计目标:让业务层依赖稳定契约,而不是依赖具体运行时细节。
## 对应文档入口
虽然大部分详细文档写在 `GFramework.Game` 侧,但阅读顺序仍然适用于本包:
- 游戏模块总览:[docs/zh-CN/game/index.md](../docs/zh-CN/game/index.md)
- 内容配置系统:[docs/zh-CN/game/config-system.md](../docs/zh-CN/game/config-system.md)
- 数据与存档:[docs/zh-CN/game/data.md](../docs/zh-CN/game/data.md)
- 设置系统:[docs/zh-CN/game/setting.md](../docs/zh-CN/game/setting.md)
- 存储系统:[docs/zh-CN/game/storage.md](../docs/zh-CN/game/storage.md)
- 序列化系统:[docs/zh-CN/game/serialization.md](../docs/zh-CN/game/serialization.md)
- 场景系统:[docs/zh-CN/game/scene.md](../docs/zh-CN/game/scene.md)
- UI 系统:[docs/zh-CN/game/ui.md](../docs/zh-CN/game/ui.md)
## 选择建议
- 选 `GFramework.Game.Abstractions`
- 你在写共享业务层、公共 feature 包、纯契约层
- 选 `GFramework.Game`
- 你需要默认实现、基础设施拼装、运行时启动入口
- 两者一起用
- 最常见。公共层依赖 abstractions应用层或引擎层依赖 runtime

View File

@ -0,0 +1,70 @@
# GFramework.Game.SourceGenerators
`GFramework.Game.SourceGenerators` 负责把 `schemas/**/*.schema.json` 转成游戏内容配置类型和表包装代码。
它服务的核心场景是:让 `YAML` 配置、`JSON Schema`、运行时加载器和工具链共享一套结构定义。
## 模块定位
这个包是编译期生成器,不是运行时库。
它会在编译期读取 schema并生成
- 配置数据类型
- 对应的表包装类型
- 与 `GFramework.Game.Config` 运行时协作的访问辅助代码
## 包关系
- 运行时:`GFramework.Game`
- 生成器:`GFramework.Game.SourceGenerators`
- 公共生成器支撑:`GFramework.SourceGenerators.Common`
如果你的项目还会使用 `[Log]``[ContextAware]` 或 Core 侧上下文注入特性,还需要同时安装
`GFramework.Core.SourceGenerators`
## 目录与输入约定
当前项目结构显示该生成器主要围绕以下代码组织:
- `Config/SchemaConfigGenerator.cs`
- `Diagnostics/ConfigSchemaDiagnostics.cs`
- `GeWuYou.GFramework.Game.SourceGenerators.targets`
消费者项目的推荐目录约定:
```text
GameProject/
├─ config/
│ └─ monster/
│ └─ slime.yaml
└─ schemas/
└─ monster.schema.json
```
默认情况下,打包产物会通过 `targets``schemas/**/*.schema.json` 纳入 `AdditionalFiles`
## 最小接入路径
```xml
<ItemGroup>
<PackageReference Include="GeWuYou.GFramework.Game" Version="x.y.z" />
<PackageReference Include="GeWuYou.GFramework.Game.SourceGenerators"
Version="x.y.z"
PrivateAssets="all"
ExcludeAssets="runtime" />
</ItemGroup>
```
如果你在仓库内用 `ProjectReference` 调试,仍需要把对应 `targets` 接进消费者项目。
## 什么时候使用它
- 你想把静态游戏内容维护成 `YAML`
- 你希望在编译期拿到强类型配置访问入口
- 你希望运行时加载、schema 校验和编辑工具链共用同一份结构定义
## 对应文档
- 配置系统:[`../docs/zh-CN/game/config-system.md`](../docs/zh-CN/game/config-system.md)
- 源码生成器总览:[`../docs/zh-CN/source-generators/index.md`](../docs/zh-CN/source-generators/index.md)

View File

@ -1,16 +1,344 @@
# GFramework.Game
GFramework 框架的游戏通用模块,提供游戏开发常用的功能
`GFramework.Game``GFramework` 面向游戏项目的运行时层
## 主要功能
它建立在 `GFramework.Core``GFramework.Game.Abstractions` 之上提供可直接落地的实现与基类覆盖静态内容配置、数据与存档、设置、场景路由、UI 路由、序列化、文件存储、状态机扩展等常见游戏运行时需求。
- **Settings** - 游戏设置系统,支持设置分类和配置应用
如果你的项目只需要“契约”而不想带入实现,请依赖 `GFramework.Game.Abstractions`。如果你的项目需要真正可运行的默认实现或基类,请依赖本包。
## 依赖关系
## 包定位
- 依赖 GFramework.Core
- 依赖 GFramework.Core.Abstractions
- 面向使用者的默认运行时实现层,不是纯接口包。
- 适合作为游戏项目启动层、基础设施层、引擎适配层之上的通用运行时底座。
- 当前最成熟、最明确的能力集中在:
- 静态内容配置:`Config/`
- 数据与存档:`Data/`
- 设置系统:`Setting/`
- 场景与 UI 路由基类:`Scene/``UI/`
- 序列化与文件存储:`Serializer/``Storage/`
## 详细文档
## 与相邻包的关系
参见 [docs/zh-CN/game/](../docs/zh-CN/game/) 目录下的详细文档。
- `GFramework.Core`
- 本包直接依赖它。
- 提供架构、上下文注入、事件、日志、系统/模型/utility 生命周期等底层能力。
- `GFramework.Game.Abstractions`
- 本包的上游契约层。
- 本包中的 `FileStorage``SettingsModel<TRepository>``SaveRepository<TSaveData>``YamlConfigLoader``SceneRouterBase``UiRouterBase` 等都在实现这里定义的接口。
- `GFramework.Game.SourceGenerators`
- 主要与配置系统配合使用。
- 当你需要 schema 驱动的 YAML 配表时,运行时包是 `GFramework.Game`,生成时代码由 source generators 补齐。
- 引擎适配包或项目内适配层
- 本包提供的是“引擎无关”的核心逻辑和基类。
- 真正和 Godot、Unity、MonoGame 等引擎对象打交道的工厂、根节点、资源注册表,通常在相邻引擎包或游戏项目内实现。
- CoreGrid 的真实接法就是这样:配置文件 IO 由 `GFramework.Godot.Config` 适配UI/Scene factory 与 root 由项目自己提供。
## 子系统地图
### `Config/`
面向静态游戏内容的只读配置系统。
- `YamlConfigLoader`
- 从文件系统根目录加载 YAML 配置表,并注册到 `IConfigRegistry`
- `ConfigRegistry`
- 运行时配置表注册表
- `GameConfigBootstrap`
- 非 `Architecture` 场景下的官方配置启动入口
- `GameConfigModule`
- `Architecture` 场景下的官方配置接入模块
- `InMemoryConfigTable<TKey, TValue>`
- 配置表默认只读承载
- `YamlConfigHotReloadOptions`
- 开发期热重载控制
这个子系统通常与 `GFramework.Game.SourceGenerators` 一起使用,而不是手写大量注册代码。
对应文档:
- [docs/zh-CN/game/config-system.md](../docs/zh-CN/game/config-system.md)
- [docs/zh-CN/game/index.md](../docs/zh-CN/game/index.md)
### `Data/`
面向可写业务数据、设置持久化与存档槽位的仓库实现。
- `DataRepository`
- 一条 `IDataLocation` 对应一份持久化对象
- `UnifiedSettingsDataRepository`
- 把多个设置 section 聚合到单一文件中
- `SaveRepository<TSaveData>`
- 面向槽位存档,支持版本迁移链
- `SaveConfiguration`
- 槽位目录、文件名、前缀等约定
CoreGrid 的真实用法:
- 设置持久化使用 `UnifiedSettingsDataRepository`
- 存档使用 `SaveRepository<GameSaveData>`
- 两者共用同一个底层存储 utility
对应文档:
- [docs/zh-CN/game/data.md](../docs/zh-CN/game/data.md)
- [docs/zh-CN/game/setting.md](../docs/zh-CN/game/setting.md)
### `Setting/`
设置生命周期编排层。
- `SettingsModel<TRepository>`
- 管理 `ISettingsData` 实例、迁移、加载、保存、重置
- 编排 applicator 的 `Apply`
- `SettingsSystem`
- 面向业务代码暴露更直接的系统级入口
- `Setting/Events/*`
- 设置初始化、应用、保存、重置相关事件
CoreGrid 的真实用法:
- 在模型模块中创建 `SettingsModel<ISettingsDataRepository>`
- 注册多个 applicator
- 启动时先 `InitializeAsync()`,再 `ApplyAll()`
- 退出时统一 `SaveAll()`
对应文档:
- [docs/zh-CN/game/setting.md](../docs/zh-CN/game/setting.md)
### `Storage/`
面向本地文件系统的基础存储。
- `FileStorage`
- 基于目录与文件的 `IStorage` 实现
- 负责路径清洗、细粒度锁、原子写入、层级 key 到目录结构的映射
- `ScopedStorage`
- 为底层存储增加前缀作用域
这部分能力经常被 `DataRepository``SaveRepository<TSaveData>``UnifiedSettingsDataRepository` 复用。
对应文档:
- [docs/zh-CN/game/storage.md](../docs/zh-CN/game/storage.md)
- [GFramework.Game/Storage/ReadMe.md](./Storage/ReadMe.md)
### `Serializer/`
- `JsonSerializer`
- 当前默认序列化实现
- 同时可作为 `ISerializer``IRuntimeTypeSerializer`
它通常先于存储和数据仓库被注册。
对应文档:
- [docs/zh-CN/game/serialization.md](../docs/zh-CN/game/serialization.md)
### `Scene/``UI/`
面向游戏导航的可复用基类,不直接绑定具体引擎。
- `SceneRouterBase`
- 依赖 `ISceneFactory``ISceneRoot`
- 提供栈式场景路由与转换处理管道
- `UiRouterBase`
- 依赖 `IUiFactory``IUiRoot`
- 提供页面栈、Overlay/Modal/Toast 等层级 UI、输入动作分发、暂停联动
- `Scene/Handler/*``UI/Handler/*`
- 默认转换处理器基类与日志处理器
CoreGrid 的真实用法:
- 项目自定义 `SceneRouter : SceneRouterBase`
- 项目自定义 `UiRouter : UiRouterBase`
- 工厂、注册表、root 都由项目或引擎适配层提供
对应文档:
- [docs/zh-CN/game/scene.md](../docs/zh-CN/game/scene.md)
- [docs/zh-CN/game/ui.md](../docs/zh-CN/game/ui.md)
### `Routing/``State/`
- `Routing/RouterBase<TRoute, TContext>`
- Scene/UI 路由共享基类
- `State/GameStateMachineSystem`
- 对核心状态机系统的游戏向封装
这两部分一般被上层子系统消费,不是多数项目的第一接入点。
## 最小接入路径
下面按最常见的四种接入目标给出最短路径。
### 1. 只想先拿到文件存储
```csharp
using GFramework.Core.Abstractions.Serializer;
using GFramework.Core.Abstractions.Storage;
using GFramework.Game.Serializer;
using GFramework.Game.Storage;
ISerializer serializer = new JsonSerializer();
IStorage storage = new FileStorage("GameData", serializer);
await storage.WriteAsync("player/profile", new { Name = "Alice", Level = 3 });
```
如果你需要逻辑隔离,再包一层 `ScopedStorage`
```csharp
var settingsStorage = new ScopedStorage(storage, "settings");
```
### 2. 接入设置和存档
运行时最小拼装顺序通常是:
1. 注册 `JsonSerializer`
2. 注册一个 `IStorage` 实现
3. 注册 `ISettingsDataRepository`
4. 创建并注册 `SettingsModel<ISettingsDataRepository>`
5. 注册 applicator
6. 注册 `SettingsSystem`
7. 注册 `ISaveRepository<TSaveData>`
示意代码:
```csharp
var serializer = new JsonSerializer();
var storage = new FileStorage("GameData", serializer);
architecture.RegisterUtility(serializer);
architecture.RegisterUtility<IStorage>(storage);
architecture.RegisterUtility<ISettingsDataRepository>(
new UnifiedSettingsDataRepository(
storage,
serializer,
new DataRepositoryOptions { BasePath = "settings", AutoBackup = true }));
architecture.RegisterModel(
new SettingsModel<ISettingsDataRepository>(
new MySettingsLocationProvider(),
architecture.Context.GetUtility<ISettingsDataRepository>())
.RegisterApplicator(new MyAudioSettingsApplicator()));
architecture.RegisterSystem<ISettingsSystem>(new SettingsSystem());
architecture.RegisterUtility<ISaveRepository<MySaveData>>(
new SaveRepository<MySaveData>(
storage,
new SaveConfiguration
{
SaveRoot = "saves",
SaveSlotPrefix = "slot_",
SaveFileName = "save.json"
}));
```
启动时:
```csharp
await settingsModel.InitializeAsync();
await settingsSystem.ApplyAll();
```
退出前:
```csharp
await settingsSystem.SaveAll();
```
CoreGrid 目前就是按这个思路接入,只是底层存储换成了 Godot 适配实现。
### 3. 接入静态 YAML 配置
如果你不走 `Architecture` 生命周期,直接使用 `GameConfigBootstrap`
```csharp
var bootstrap = new GameConfigBootstrap(
new GameConfigBootstrapOptions
{
RootPath = contentRootPath,
ConfigureLoader = static loader =>
{
loader.RegisterAllGeneratedConfigTables();
}
});
await bootstrap.InitializeAsync();
var registry = bootstrap.Registry;
```
如果你走 `Architecture`,优先使用 `GameConfigModule`,并在较早阶段安装。
这一能力几乎总是与 source generators 绑定使用。目录、schema、生成器与热重载约定请直接看
- [docs/zh-CN/game/config-system.md](../docs/zh-CN/game/config-system.md)
### 4. 接入 Scene / UI 路由
这里的最小前提不是“直接 new 一个 router”而是先补齐运行时依赖
- `ISceneFactory` / `IUiFactory`
- `ISceneRoot` / `IUiRoot`
- 具体页面或场景行为实现
然后让项目自己的 router 继承基类:
```csharp
public sealed class MySceneRouter : SceneRouterBase
{
protected override void RegisterHandlers()
{
RegisterHandler(new LoggingTransitionHandler());
}
}
public sealed class MyUiRouter : UiRouterBase
{
protected override void RegisterHandlers()
{
RegisterHandler(new GFramework.Game.UI.Handler.LoggingTransitionHandler());
}
}
```
这类 router 适合作为你的项目层或引擎适配层代码,而不是直接修改本包。
## CoreGrid 里的真实用法线索
当前仓库内CoreGrid 对本包的使用大致分成三层:
- 配置
- `CoreGridConfigHost` 使用生成表元数据与 YAML loader 完成配置注册
- 设置与存档
- `UtilityModule` 注册序列化器、底层存储、`UnifiedSettingsDataRepository``SaveRepository<GameSaveData>`
- `ModelModule` 创建 `SettingsModel<ISettingsDataRepository>` 并注册 applicator
- 路由
- `SceneRouter` 继承 `SceneRouterBase`
- `UiRouter` 继承 `UiRouterBase`
这说明本包更适合做“游戏基础设施层”,而不是把所有引擎对象耦死在包内部。
## 文档入口
- 游戏模块总览:[docs/zh-CN/game/index.md](../docs/zh-CN/game/index.md)
- 内容配置系统:[docs/zh-CN/game/config-system.md](../docs/zh-CN/game/config-system.md)
- 数据与存档:[docs/zh-CN/game/data.md](../docs/zh-CN/game/data.md)
- 设置系统:[docs/zh-CN/game/setting.md](../docs/zh-CN/game/setting.md)
- 存储系统:[docs/zh-CN/game/storage.md](../docs/zh-CN/game/storage.md)
- 序列化系统:[docs/zh-CN/game/serialization.md](../docs/zh-CN/game/serialization.md)
- 场景系统:[docs/zh-CN/game/scene.md](../docs/zh-CN/game/scene.md)
- UI 系统:[docs/zh-CN/game/ui.md](../docs/zh-CN/game/ui.md)
## 什么时候不该直接依赖本包
以下场景优先考虑只依赖 `GFramework.Game.Abstractions`
- 你在做纯领域层、协议层或可复用 feature 包,只想引用接口和数据契约
- 你已经有自己的配置、存储、路由实现,只想复用统一契约
- 你不希望业务程序集带入 `Newtonsoft.Json``YamlDotNet` 等运行时依赖

140
README.md
View File

@ -1,6 +1,6 @@
# GFramework
> 面向游戏开发场景的模块化 C# 框架,核心能力与具体引擎解耦,可按需组合 Core / Game / Godot / Source Generators
> 面向游戏开发场景的模块化 C# 框架,按运行时、抽象层、引擎集成和源码生成器拆分能力
[![NuGet Core](https://img.shields.io/badge/NuGet-GeWuYou.GFramework.Core-2C7BE5)](https://www.nuget.org/packages/GeWuYou.GFramework.Core)
[![NuGet Meta](https://img.shields.io/badge/NuGet-GeWuYou.GFramework-1F9D55)](https://www.nuget.org/packages/GeWuYou.GFramework)
@ -8,84 +8,87 @@
[![.NET](https://img.shields.io/badge/.NET-8.0+-purple)](https://dotnet.microsoft.com/)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue)](LICENSE)
[![zread](https://img.shields.io/badge/Ask_Zread-_.svg?style=flat-square&color=00b0aa&labelColor=000000&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9IjAgMCAxNiAxNiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTQuOTYxNTYgMS42MDAxSDIuMjQxNTZDMS44ODgxIDEuNjAwMSAxLjYwMTU2IDEuODg2NjQgMS42MDE1NiAyLjI0MDFWNC45NjAxQzEuNjAxNTYgNS4zMTM1NiAxLjg4ODEgNS42MDAxIDIuMjQxNTYgNS42MDAxSDQuOTYxNTZDNS5zMTUwMiA1LjYwMDEgNS42MDE1NiA1LjMxMzU2IDUuNjAxNTYgNC45NjAxVjIuMjQwMUM1LjYwMTU2IDEuODg2NjQgNS4zMTUwMiAxLjYwMDEgNC45NjE1NiAxLjYwMDFaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00Ljk2MTU2IDEwLjM5OTlIMi4yNDE1NkMxLjg4ODEgMTAuMzk5OSAxLjYwMTU2IDEwLjY4NjQgMS42MDE1NiAxMS4wMzk5VjEzLjc1OTlDMS42MDE1NiAxNC4xMTM0IDEuODg4MSAxNC4zOTk5IDIuMjQxNTYgMTQuMzk5OUg0Ljk2MTU2QzUuMzE1MDIgMTQuMzk5OSA1LjYwMTU2IDE0LjExMzQgNS42MDE1NiAxMy43NTk5VjExLjAzOTlDNS42MDE1NiAxMC42ODY0IDUuMzE1MDIgMTAuMzk5OSA0Ljk2MTU2IDEwLjM5OTlaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik0xMy43NTg0IDEuNjAwMUgxMS4wMzg0QzEwLjY4NSAxLjYwMDEgMTAuMzk4NCAxLjg4NjY0IDEwLjM5ODQgMi4yNDAxVjQuOTYwMUMxMC4zOTg0IDUuMzEzNTYgMTAuNjg1IDUuNjAwMSAxMS4wMzg0IDUuNjAwMUgxMy43NTg0QzE0LjExMTkgNS42MDAxIDE0LjM5ODQgNS4zMTM1NiAxNC4zOTg0IDQuOTYwMVYyLjI0MDFDMTQuMzk4NCAxLjg4NjY0IDE0LjExMTkgMS42MDAxIDEzLjc1ODQgMS42MDAxWiIgZmlsbD0iI2ZmZiIvPgo8cGF0aCBkPSJNNCAxMkwxMiA0TDQgMTJaIiBmaWxsPSIjZmZmIi8%2BCjxwYXRoIGQ9Ik00IDEyTDEyIDQiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K&logoColor=ffffff)](https://zread.ai/GeWuYou/GFramework)
---
## 项目简介
## 从哪里开始
GFramework 采用清晰分层与模块化设计,强调:
- 第一次接触框架:[`docs/zh-CN/getting-started/index.md`](docs/zh-CN/getting-started/index.md)
- 想先跑一个最小例子:[`docs/zh-CN/getting-started/quick-start.md`](docs/zh-CN/getting-started/quick-start.md)
- 已经知道要用哪个模块:直接进入对应模块目录下的 `README.md`
- **架构分层Architecture / Model / System / Utility**
- **命令与查询分离CQRS**
- **类型安全事件机制**
- **可绑定属性与响应式数据流**
- **可扩展的 IOC/生命周期管理**
- **基于 Roslyn 的源码生成能力**
## 模块地图
项目灵感参考自 [QFramework](https://github.com/liangxiegame/QFramework),并在模块边界、工程组织和可扩展性方面进行了持续重构。
## 功能模块
| 模块 | 说明 | 文档 |
|-------------------------------------------------------------------------|-----------------------------------------|---------------------------------------------------|
| `GFramework.Core` | 平台无关的核心架构能力架构、命令、查询、事件、属性、IOC、日志等 | [查看](GFramework.Core/README.md) |
| `GFramework.Core.Abstractions` | Core 对应的抽象接口定义 | [查看](GFramework.Core.Abstractions/README.md) |
| `GFramework.Game` | 游戏业务侧扩展状态、配置、存储、UI 等) | [查看](GFramework.Game/README.md) |
| `GFramework.Game.Abstractions` | Game 模块抽象接口定义 | [查看](GFramework.Game.Abstractions/README.md) |
| `GFramework.Godot` | Godot 集成层(节点扩展、场景/设置/存储适配等) | [查看](GFramework.Godot/README.md) |
| `GFramework.Cqrs` / `GFramework.Cqrs.Abstractions` | CQRS runtime、契约与 handler 注册基础设施 | [查看](docs/zh-CN/core/cqrs.md) |
| `GFramework.Core.SourceGenerators` | Core 侧源码生成器(日志、枚举扩展、规则、模块注册等) | [查看](GFramework.Core.SourceGenerators/README.md) |
| `GFramework.Game.SourceGenerators` / `GFramework.Cqrs.SourceGenerators` | 游戏配置 schema 与 CQRS handler registry 生成器 | [查看](docs/zh-CN/source-generators/index.md) |
| `GFramework.Godot.SourceGenerators` | Godot 场景下的源码生成器扩展 | [查看](GFramework.Godot.SourceGenerators/README.md) |
| 模块 | 作用 | 入口 |
| --- | --- | --- |
| `GFramework.Core` | 架构、命令、查询、事件、状态、日志、资源、协程等基础运行时 | [README](GFramework.Core/README.md) |
| `GFramework.Core.Abstractions` | `Core` 对应的契约层,适合面向接口开发或做模块拆分 | [README](GFramework.Core.Abstractions/README.md) |
| `GFramework.Cqrs` | 新版 CQRS runtime提供 request dispatcher、notification publish 与 handler 注册 | [README](GFramework.Cqrs/README.md) |
| `GFramework.Cqrs.Abstractions` | CQRS 消息、处理器、pipeline 行为等契约 | [README](GFramework.Cqrs.Abstractions/README.md) |
| `GFramework.Game` | 面向游戏项目的配置、数据、路由、场景、UI、设置和存储运行时 | [README](GFramework.Game/README.md) |
| `GFramework.Game.Abstractions` | `Game` 对应的契约层 | [README](GFramework.Game.Abstractions/README.md) |
| `GFramework.Godot` | Godot 集成层负责把框架能力接入节点、场景、UI、设置与存储 | [README](GFramework.Godot/README.md) |
| `GFramework.Ecs.Arch` | Arch ECS 集成 | [README](GFramework.Ecs.Arch/README.md) |
| `GFramework.Core.SourceGenerators` | Core 侧通用源码生成器与分析器 | [README](GFramework.Core.SourceGenerators/README.md) |
| `GFramework.Game.SourceGenerators` | 游戏内容配置 schema 生成器 | [README](GFramework.Game.SourceGenerators/README.md) |
| `GFramework.Cqrs.SourceGenerators` | CQRS handler registry 生成器 | [README](GFramework.Cqrs.SourceGenerators/README.md) |
| `GFramework.Godot.SourceGenerators` | Godot 场景专用源码生成器 | [README](GFramework.Godot.SourceGenerators/README.md) |
## 文档导航
- 入门教程:[`docs/zh-CN/tutorials/getting-started.md`](docs/zh-CN/tutorials/getting-started.md)
- Godot 集成:[`docs/zh-CN/godot/index.md`](docs/zh-CN/godot/index.md)
- 进阶模式:[`docs/zh-CN/core/index.md`](docs/zh-CN/core/index.md)
- 最佳实践:[`docs/zh-CN/best-practices/architecture-patterns.md`](docs/zh-CN/best-practices/architecture-patterns.md)
- API 参考:[`docs/zh-CN/api-reference/`](docs/zh-CN/api-reference/)
仓库根 README 与文档站点保持同一套栏目命名:
> 如果你更偏好按模块阅读,建议从各子项目 `README.md` 开始,再回到 `docs/` 查阅专题文档。
- 入门指南:[`docs/zh-CN/getting-started/index.md`](docs/zh-CN/getting-started/index.md)
- Core[`docs/zh-CN/core/index.md`](docs/zh-CN/core/index.md)
- Game[`docs/zh-CN/game/index.md`](docs/zh-CN/game/index.md)
- Godot[`docs/zh-CN/godot/index.md`](docs/zh-CN/godot/index.md)
- 教程:[`docs/zh-CN/tutorials/index.md`](docs/zh-CN/tutorials/index.md)
- 源码生成器:[`docs/zh-CN/source-generators/index.md`](docs/zh-CN/source-generators/index.md)
- ECS[`docs/zh-CN/ecs/index.md`](docs/zh-CN/ecs/index.md)
- 抽象接口:[`docs/zh-CN/abstractions/index.md`](docs/zh-CN/abstractions/index.md)
- 最佳实践:[`docs/zh-CN/best-practices/index.md`](docs/zh-CN/best-practices/index.md)
- API 参考:[`docs/zh-CN/api-reference/index.md`](docs/zh-CN/api-reference/index.md)
- FAQ[`docs/zh-CN/faq.md`](docs/zh-CN/faq.md)
- 故障排查:[`docs/zh-CN/troubleshooting.md`](docs/zh-CN/troubleshooting.md)
- 贡献:[`docs/zh-CN/contributing.md`](docs/zh-CN/contributing.md)
## 包选择说明(避免混淆)
## 包选择
- **`GeWuYou.GFramework`**聚合元包Meta Package用于一键引入常用能力集合适合快速试用或原型阶段。
- **`GeWuYou.GFramework.Core`**:核心起步包,适合希望按模块精细控制依赖的项目(推荐生产项目从此起步)。
- `GeWuYou.GFramework`
当前是聚合元包,只聚合 `GFramework.Core``GFramework.Game` 这两层运行时,适合快速试用。
- `GeWuYou.GFramework.Core`
推荐的最小起步包。先从核心运行时开始,再按需叠加 `Cqrs``Game``Godot` 和 Source Generators。
- `GeWuYou.GFramework.*.Abstractions`
适合需要单独依赖契约层、插件化、测试替身或多模块解耦的场景。
- `GeWuYou.GFramework.*.SourceGenerators`
只在需要编译期生成代码时安装,版本应与运行时包保持一致。
如果你已明确技术栈建议优先按模块安装Core / Cqrs / Game / Godot / Source Generators避免不必要依赖。
## 快速安装
按实际需求选择依赖:
## 最小安装组合
```bash
# 核心能力(推荐最小起步
# 最小起步
dotnet add package GeWuYou.GFramework.Core
dotnet add package GeWuYou.GFramework.Core.Abstractions
# CQRS
# 新版 CQRS
dotnet add package GeWuYou.GFramework.Cqrs
dotnet add package GeWuYou.GFramework.Cqrs.Abstractions
# 游戏扩展
# 游戏层运行时
dotnet add package GeWuYou.GFramework.Game
dotnet add package GeWuYou.GFramework.Game.Abstractions
# Godot 集成(仅 Godot 项目需要)
# Godot 集成
dotnet add package GeWuYou.GFramework.Godot
# 按场景选择源码生成器(可选,但推荐)
# 按需安装源码生成器
dotnet add package GeWuYou.GFramework.Core.SourceGenerators
dotnet add package GeWuYou.GFramework.Game.SourceGenerators
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
dotnet add package GeWuYou.GFramework.Godot.SourceGenerators
```
## 可选模块导入
## 可选全局 using
发布后的运行时包支持可选的模块级自动导入,但默认关闭,避免在普通项目里无意污染命名空间。
在 NuGet 消费项目中显式开启:
NuGet 消费项目可显式开启模块级自动导入:
```xml
<PropertyGroup>
@ -93,9 +96,7 @@ dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
</PropertyGroup>
```
启用后,项目已引用的 GFramework 运行时模块会通过 `buildTransitive` 自动注入其推荐命名空间。
如果某几个命名空间不想导入,可以局部排除:
如果只想排除部分命名空间:
```xml
<ItemGroup>
@ -104,45 +105,36 @@ dotnet add package GeWuYou.GFramework.Cqrs.SourceGenerators
</ItemGroup>
```
> 该能力面向 NuGet 包消费场景。若你在本地解决方案中直接使用 `ProjectReference`,仍建议保留自己的 `GlobalUsings.cs` 或手写
`using`
> 该能力主要面向 NuGet 消费场景。仓库内 `ProjectReference` 方式仍建议显式维护自己的 `GlobalUsings.cs`
## 仓库结构
```text
GFramework.sln
├─ GFramework.Cqrs/
├─ GFramework.Cqrs.Abstractions/
├─ GFramework.Core/
├─ GFramework.Core.Abstractions/
├─ GFramework.Core.SourceGenerators/
├─ GFramework.Core.SourceGenerators.Abstractions/
├─ GFramework.Cqrs/
├─ GFramework.Cqrs.Abstractions/
├─ GFramework.Game/
├─ GFramework.Game.Abstractions/
├─ GFramework.Game.SourceGenerators/
├─ GFramework.Godot/
├─ GFramework.Godot.SourceGenerators/
├─ GFramework.Ecs.Arch/
├─ GFramework.Core.SourceGenerators/
├─ GFramework.Game.SourceGenerators/
├─ GFramework.Cqrs.SourceGenerators/
├─ GFramework.Godot.SourceGenerators/
├─ GFramework.SourceGenerators.Common/
├─ docs/
└─ docfx/
└─ docs/
```
## 兼容性
- **运行时/工具链**:基于 .NET 生态,具体以各项目 `*.csproj``TargetFramework` 为准。
- **引擎集成**:当前提供 Godot 集成模块Core 层可迁移至其他 .NET 场景。
## 贡献
欢迎提交 Issue 与 Pull Request
提交功能或行为变更时,请把代码、测试和文档一起更新:
1. 提交 Issue 时请优先选择对应模板:`Bug Report / 缺陷报告``Feature Request / 功能建议``Documentation / 文档改进``Question / 使用咨询`
2. 提交前先搜索现有 Issues并阅读相关 README、文档或排障页面
3. Fork 本仓库并创建特性分支
4. 补充必要的测试或文档更新
5. 提交 PR描述变更背景、方案与验证结果
1. 先阅读对应模块目录下的 `README.md`
2. 如果改动影响采用路径、安装方式、公共 API 或目录结构,同时更新 `docs/zh-CN/`
3. 对跨模块或多阶段任务,维护 `local-plan/todos/``local-plan/traces/`
## 许可证
项目采用 [Apache License 2.0](LICENSE)。
仓库当前采用 [Apache License 2.0](LICENSE)。

View File

@ -116,6 +116,7 @@ export default defineConfig({
text: '更多',
items: [
{ text: 'ECS', link: '/zh-CN/ecs/' },
{ text: '抽象接口', link: '/zh-CN/abstractions/' },
{ text: '源码生成器', link: '/zh-CN/source-generators' },
{ text: '最佳实践', link: '/zh-CN/best-practices/' },
{ text: 'API 参考', link: '/zh-CN/api-reference' },

View File

@ -0,0 +1,20 @@
# 抽象接口
`GFramework.*.Abstractions` 用来承载跨模块协作所需的契约,而不是运行时实现。
适合阅读这部分内容的场景:
- 你要做模块拆分,只想依赖接口,不想直接引用完整运行时
- 你要为测试、编辑器工具或插件提供替身实现
- 你在维护生成器、适配层或二次封装,需要先理解契约边界
## 阅读顺序
- Core 抽象层:[`core-abstractions.md`](./core-abstractions.md)
- Game 抽象层:[`game-abstractions.md`](./game-abstractions.md)
## 使用建议
- 如果你只是想直接使用框架功能,优先从对应运行时模块的 `README.md` 和栏目页开始。
- 只有在明确需要“契约层而非实现层”时,才单独依赖 `*.Abstractions` 包。
- 抽象层页面会解释接口分组与职责;真正的安装和采用路径,仍应以运行时模块 README 与 `getting-started` 为主。

View File

@ -1,359 +1,107 @@
# 架构概览
GFramework 采用经典的五层架构模式,结合 CQRS 和事件驱动设计,为游戏开发提供清晰、可维护的架构基础。
## 核心架构模式
### 五层架构
```
┌─────────────────────────────────────────┐
│ View / UI │ ← 用户界面层
├─────────────────────────────────────────┤
│ Controller │ ← 控制层
├─────────────────────────────────────────┤
│ System │ ← 业务逻辑层
├─────────────────────────────────────────┤
│ Model │ ← 数据层
├─────────────────────────────────────────┤
│ Utility │ ← 工具层
└─────────────────────────────────────────┘
```
### 跨层操作机制
```
Command ──┐
Query ──┼──→ 跨层操作(修改/查询数据)
Event ──┘
```
### 生命周期阶段
```
初始化Init → BeforeUtilityInit → AfterUtilityInit → BeforeModelInit → AfterModelInit → BeforeSystemInit → AfterSystemInit → Ready
销毁Destroy → Destroying → Destroyed
```
## 核心组件详解
### 1. Architecture架构
应用的中央调度器,负责管理所有组件的生命周期。
```csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册所有组件
RegisterModel(new PlayerModel());
RegisterSystem(new CombatSystem());
RegisterUtility(new StorageUtility());
}
}
```
**主要职责:**
- 组件注册和管理
- 生命周期协调
- 依赖注入
- 跨组件通信协调
### 2. Model数据模型
应用的状态存储层,只负责数据的存储和管理。
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<string> Name { get; } = new("Player");
protected override void OnInit()
{
// 监听自身数据变化
Health.Register(OnHealthChanged);
}
private void OnHealthChanged(int newHealth)
{
if (newHealth <= 0)
this.SendEvent(new PlayerDiedEvent());
}
}
```
**设计原则:**
- 只存储数据,不包含业务逻辑
- 使用 BindableProperty 实现响应式数据
- 通过事件通知数据变化
### 3. System业务系统
应用的业务逻辑处理层。
```csharp
public class CombatSystem : AbstractSystem
{
protected override void OnInit()
{
// 订阅相关事件
this.GetEvent<AttackEvent>().Register(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var attacker = e.Attacker;
var target = e.Target;
// 计算伤害
var damage = CalculateDamage(attacker, target);
// 更新目标生命值
target.Health.Value -= damage;
// 发送伤害事件
this.SendEvent(new DamageEvent(target, damage));
}
private int CalculateDamage(Entity attacker, Entity target)
{
return Mathf.Max(1, attacker.Attack.Value - target.Defense.Value);
}
}
```
**设计原则:**
- 处理业务逻辑,不直接存储数据
- 通过事件与其他组件通信
- 从 Model 获取数据,向 Model 发送更新
### 4. Controller控制器
连接 UI 和业务逻辑的桥梁。
```csharp
public class PlayerController : IController
{
private IArchitecture _architecture;
private PlayerModel _playerModel;
public PlayerController(IArchitecture architecture)
{
_architecture = architecture;
_playerModel = architecture.GetModel<PlayerModel>();
// 监听模型变化并更新 UI
_playerModel.Health.RegisterWithInitValue(UpdateHealthDisplay);
}
public void OnPlayerInput(Vector2 direction)
{
// 将用户输入转换为命令
_architecture.SendCommand(new MovePlayerCommand { Direction = direction });
}
private void UpdateHealthDisplay(int health)
{
// 更新 UI 显示
Console.WriteLine($"Player Health: {health}");
}
}
```
**核心功能:**
- 接收用户输入
- 发送命令到系统
- 监听模型变化更新 UI
- 协调 UI 和业务逻辑
### 5. Utility工具类
提供无状态的辅助功能。
```csharp
public class StorageUtility : IUtility
{
public void SaveData<T>(string key, T data)
{
// 实现数据保存逻辑
}
public T LoadData<T>(string key, T defaultValue = default)
{
// 实现数据加载逻辑
return defaultValue;
}
}
```
**使用场景:**
- 数据存储和读取
- 数学计算工具
- 字符串处理
- 网络通信辅助
## 通信机制
### 1. Command命令
用于修改应用状态的操作:
```csharp
public class MovePlayerCommand : AbstractCommand
{
public Vector2 Direction { get; set; }
protected override void OnDo()
{
// 执行移动逻辑
this.SendEvent(new PlayerMovedEvent { Position = CalculateNewPosition() });
}
}
```
### 2. Query查询
用于查询应用状态:`
```csharp
public class GetPlayerHealthQuery : AbstractQuery<int>
{
protected override int OnDo()
{
var playerModel = this.GetModel<PlayerModel>();
return playerModel.Health.Value;
}
}
```
### 3. Event事件
组件间通信的主要机制:`
```
// 发送事件
this.SendEvent(new PlayerDiedEvent());
// 监听事件
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
```
## 响应式编程
### BindableProperty
```csharp
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<string> Name { get; } = new("Player");
}
// 使用方式
playerModel.Health.Value = 50; // 自动触发所有监听器
playerModel.Health.Register(newValue => {
Console.WriteLine($"Health changed to: {newValue}");
});
```
### 数据绑定优势
- **自动更新**:数据变化自动通知监听者
- **内存安全**:自动管理监听器生命周期
- **类型安全**:编译时类型检查
- **性能优化**:只在值真正改变时触发
## 最佳实践
### 1. 分层职责明确
```csharp
// ✅ 正确Model 只存储数据
public class PlayerModel : AbstractModel
{
public BindableProperty<int> Health { get; } = new(100);
}
// ❌ 错误Model 包含业务逻辑
public class PlayerModel : AbstractModel
{
public void TakeDamage(int damage) // 业务逻辑应该在 System 中
{
Health.Value -= damage;
}
}
```
### 2. 事件驱动设计
```csharp
// ✅ 正确:使用事件解耦
public class CombatSystem : AbstractSystem
{
private void OnPlayerAttack(PlayerAttackEvent e)
{
// 处理攻击逻辑
this.SendEvent(new EnemyDamagedEvent { Damage = CalculateDamage() });
}
}
// ❌ 错误:直接调用其他组件
public class CombatSystem : AbstractSystem
{
private void OnPlayerAttack(PlayerAttackEvent e)
{
var enemySystem = this.GetSystem<EnemySystem>(); // 紧耦合
enemySystem.TakeDamage(CalculateDamage());
}
}
```
### 3. 命令查询分离
```csharp
// ✅ 正确:明确区分命令和查询
public class MovePlayerCommand : AbstractCommand { } // 修改状态
public class GetPlayerPositionQuery : AbstractQuery<Vector2> { } // 查询状态
// ❌ 错误:混合读写操作
public class PlayerManager
{
public void MoveAndGetPosition(Vector2 direction, out Vector2 position) // 职责不清
{
// ...
}
}
```
## 架构优势
### 1. 可维护性
- 清晰的职责分离
- 松耦合的组件设计
- 易于定位和修复问题
### 2. 可测试性
- 组件可独立测试
- 依赖可轻松模拟
- 支持单元测试和集成测试
### 3. 可扩展性
- 新功能通过添加组件实现
- 现有组件无需修改
- 支持插件化架构
### 4. 团队协作
- 统一的架构规范
- 易于新人上手
- 减少代码冲突
# 入门指南
这一部分只回答三个问题:
1. `GFramework` 由哪些模块组成
2. 第一次接入应该从哪个包开始
3. 最小可运行路径是什么
如果你还没决定具体用法,先阅读本栏目;如果你已经明确要用某个模块,直接进入对应模块目录下的 `README.md` 会更快。
## 推荐起步路径
### 只想先把架构跑起来
`Core` 开始:
- `GeWuYou.GFramework.Core`
- `GeWuYou.GFramework.Core.Abstractions`
这组包提供:
- `Architecture`
- `Model` / `System` / `Utility`
- 旧版 `Command` / `Query` 执行器
- 事件、属性、状态机、状态管理、资源、日志、协程等基础设施
对应文档:
- [`../core/index.md`](../core/index.md)
- [`quick-start.md`](./quick-start.md)
### 想用新版 CQRS
`Core` 基础上补:
- `GeWuYou.GFramework.Cqrs`
- `GeWuYou.GFramework.Cqrs.Abstractions`
这组包提供:
- 统一 request dispatcher
- notification publish
- pipeline behaviors
- handler 注册与反射回退机制
对应文档:
- [`../core/cqrs.md`](../core/cqrs.md)
- 仓库内模块入口:`GFramework.Cqrs/README.md`
### 想做游戏运行时
`Core` 基础上按需补:
- `GeWuYou.GFramework.Game`
- `GeWuYou.GFramework.Game.Abstractions`
这组包提供:
- 内容配置系统
- 数据存取与设置
- Scene / UI / Routing 抽象与运行时
- 文件存储和序列化
对应文档:
- [`../game/index.md`](../game/index.md)
- 仓库内模块入口:`GFramework.Game/README.md`
### 想接入 Godot
继续叠加:
- `GeWuYou.GFramework.Godot`
对应文档:
- [`../godot/index.md`](../godot/index.md)
- 仓库内模块入口:`GFramework.Godot/README.md`
## Source Generators 什么时候装
只在需要编译期生成代码时再装:
- `GeWuYou.GFramework.Core.SourceGenerators`
- `GeWuYou.GFramework.Game.SourceGenerators`
- `GeWuYou.GFramework.Cqrs.SourceGenerators`
- `GeWuYou.GFramework.Godot.SourceGenerators`
典型场景:
- 自动生成日志、上下文绑定、模块注册代码
- 从 `schema` 生成游戏配置类型
- 为 CQRS handlers 生成注册表
- 生成 Godot 节点、场景和 UI 包装代码
## 建议阅读顺序
1. [`quick-start.md`](./quick-start.md)
2. 你准备使用的模块 README
3. 对应栏目页,例如 `core/``game/``godot/`
4. 需要更完整示例时,再进入 `tutorials/`
## 注意
- 旧文档里有一些早期示例已经和当前 API 漂移。本栏目以后只保留经过代码或测试核对的最小路径。
- 若根 README、模块 README 与某篇专题页冲突,以模块 README 和当前代码为准。

View File

@ -1,323 +1,105 @@
# 快速开始
指南将帮助您快速构建第一个基于 GFramework 的应用程序
页给出一个只依赖 `Core` 的最小路径,用来确认你已经成功接入 `Architecture``Model``System` 与旧版命令执行器
## 1. 创建项目架构
> 说明:当前仓库同时存在旧版 `Command` / `Query` 执行器与新版 `CQRS` runtime。本页故意先用最短路径说明基础架构如何跑起来如果你要写新功能随后应继续阅读 [`../core/cqrs.md`](../core/cqrs.md)。
首先定义您的应用架构:
## 1. 安装最小依赖
```bash
dotnet add package GeWuYou.GFramework.Core
dotnet add package GeWuYou.GFramework.Core.Abstractions
```
## 2. 定义一个最小架构
```csharp
using GFramework.Core.Architecture;
using GFramework.Core.Architectures;
public class GameArchitecture : Architecture
public sealed class CounterArchitecture : Architecture
{
protected override void Init()
protected override void OnInitialize()
{
// 注册模型 - 存储应用状态
RegisterModel(new PlayerModel());
RegisterModel(new GameStateModel());
// 注册系统 - 处理业务逻辑
RegisterSystem(new PlayerSystem());
RegisterSystem(new GameLogicSystem());
// 注册工具类 - 提供辅助功能
RegisterUtility(new StorageUtility());
RegisterModel(new CounterModel());
RegisterSystem(new CounterSystem());
}
}
```
## 2. 定义数据模型
这里要点只有两个:
创建您的数据模型:
- 架构入口是 `Architecture`
- 当前版本使用 `protected override void OnInitialize()` 注册模型、系统和工具
## 3. 定义一个模型
```csharp
public class PlayerModel : AbstractModel
using GFramework.Core.Model;
using GFramework.Core.Property;
public sealed class CounterModel : AbstractModel
{
// 使用可绑定属性实现响应式数据
public BindableProperty<string> Name { get; } = new("Player");
public BindableProperty<int> Health { get; } = new(100);
public BindableProperty<int> Score { get; } = new(0);
public BindableProperty<int> Count { get; } = new(0);
protected override void OnInit()
{
// 监听健康值变化
Health.Register(OnHealthChanged);
}
private void OnHealthChanged(int newHealth)
{
if (newHealth <= 0)
{
this.SendEvent(new PlayerDiedEvent());
}
}
}
public class GameStateModel : AbstractModel
{
public BindableProperty<bool> IsGameRunning { get; } = new(false);
public BindableProperty<int> CurrentLevel { get; } = new(1);
}
```
## 3. 实现业务逻辑
`BindableProperty<T>` 适合承载可观察状态;如果你只需要一个最小例子,保持 `OnInit()` 为空即可。
创建处理业务逻辑的系统:
## 4. 定义一个系统
```csharp
public class PlayerSystem : AbstractSystem
using GFramework.Core.Systems;
public sealed class CounterSystem : AbstractSystem
{
protected override void OnInit()
{
// 监听玩家输入事件
this.RegisterEvent<PlayerMoveEvent>(OnPlayerMove);
this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
}
private void OnPlayerMove(PlayerMoveEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 处理移动逻辑
Console.WriteLine($"Player moved to {e.Direction}");
}
private void OnPlayerAttack(PlayerAttackEvent e)
{
var playerModel = this.GetModel<PlayerModel>();
// 处理攻击逻辑
playerModel.Score.Value += 10;
this.SendEvent(new EnemyDamagedEvent { Damage = 25 });
}
}
public class GameLogicSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<EnemyDamagedEvent>(OnEnemyDamaged);
this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
}
private void OnEnemyDamaged(EnemyDamagedEvent e)
{
Console.WriteLine($"Enemy took {e.Damage} damage");
// 检查是否需要升级关卡
CheckLevelProgress();
}
private void OnPlayerDied(PlayerDiedEvent e)
{
var gameState = this.GetModel<GameStateModel>();
gameState.IsGameRunning.Value = false;
Console.WriteLine("Game Over!");
}
private void CheckLevelProgress()
{
// 实现关卡进度检查逻辑
}
}
```
## 4. 定义事件
创建应用中使用的事件:
## 5. 定义一个命令
```csharp
public class PlayerMoveEvent : IEvent
{
public Vector2 Direction { get; set; }
}
using GFramework.Core.Command;
public class PlayerAttackEvent : IEvent
public sealed class IncreaseCountCommand : AbstractCommand
{
public Vector2 TargetPosition { get; set; }
}
public class PlayerDiedEvent : IEvent
{
// 玩家死亡事件
}
public class EnemyDamagedEvent : IEvent
{
public int Damage { get; set; }
protected override void OnExecute()
{
var model = GetModel<CounterModel>();
model.Count.Value += 1;
}
}
```
## 5. 创建控制器
`AbstractCommand` 继承自 `ContextAwareBase`,所以命令内部可以直接访问 `GetModel<T>()``GetSystem<T>()` 等上下文方法。
实现控制器来连接 UI 和业务逻辑:
## 6. 初始化并执行
```csharp
using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule;
var architecture = new CounterArchitecture();
architecture.Initialize();
[ContextAware]
public partial class GameController : IController
{
private PlayerModel _playerModel;
private GameStateModel _gameStateModel;
architecture.Context.SendCommand(new IncreaseCountCommand());
public void Initialize()
{
_playerModel = this.GetModel<PlayerModel>();
_gameStateModel = this.GetModel<GameStateModel>();
// 初始化事件监听
InitializeEventListeners();
}
private void InitializeEventListeners()
{
// 监听模型变化并更新 UI
_playerModel.Health.RegisterWithInitValue(OnHealthChanged);
_playerModel.Score.RegisterWithInitValue(OnScoreChanged);
_gameStateModel.IsGameRunning.Register(OnGameStateChanged);
}
public void StartGame()
{
_gameStateModel.IsGameRunning.Value = true;
this.SendEvent(new GameStartEvent());
Console.WriteLine("Game started!");
}
public void MovePlayer(Vector2 direction)
{
this.SendCommand(new MovePlayerCommand { Direction = direction });
}
public void PlayerAttack(Vector2 target)
{
this.SendCommand(new AttackCommand { TargetPosition = target });
}
// UI 更新回调
private void OnHealthChanged(int health)
{
UpdateHealthDisplay(health);
}
private void OnScoreChanged(int score)
{
UpdateScoreDisplay(score);
}
private void OnGameStateChanged(bool isRunning)
{
UpdateGameStatusDisplay(isRunning);
}
private void UpdateHealthDisplay(int health)
{
// 更新血条 UI
Console.WriteLine($"Health: {health}");
}
private void UpdateScoreDisplay(int score)
{
// 更新分数显示
Console.WriteLine($"Score: {score}");
}
private void UpdateGameStatusDisplay(bool isRunning)
{
// 更新游戏状态显示
Console.WriteLine($"Game running: {isRunning}");
}
}
var count = architecture.Context.GetModel<CounterModel>().Count.Value;
Console.WriteLine(count); // 1
```
## 6. 定义命令
如果你能走通这一步,说明以下链路已经成立:
创建命令来封装用户操作:
```csharp
public class MovePlayerCommand : AbstractCommand
{
public Vector2 Direction { get; set; }
protected override void OnDo()
{
// 发送移动事件
this.SendEvent(new PlayerMoveEvent { Direction = Direction });
}
}
public class AttackCommand : AbstractCommand
{
public Vector2 TargetPosition { get; set; }
protected override void OnDo()
{
// 发送攻击事件
this.SendEvent(new PlayerAttackEvent { TargetPosition = TargetPosition });
}
}
```
## 7. 运行应用
现在让我们运行这个简单的应用:
```csharp
class Program
{
static void Main(string[] args)
{
// 创建并初始化架构
var architecture = new GameArchitecture();
architecture.Initialize();
// 创建控制器
var gameController = new GameController();
gameController.Initialize();
// 开始游戏
gameController.StartGame();
// 模拟玩家操作
gameController.MovePlayer(new Vector2(1, 0));
gameController.PlayerAttack(new Vector2(5, 5));
// 模拟玩家受伤
var playerModel = architecture.GetModel<PlayerModel>();
playerModel.Health.Value = 50;
// 模拟玩家死亡
playerModel.Health.Value = 0;
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
```
## 8. 运行结果
执行程序后,您应该看到类似以下输出:
```
Game started!
Game running: True
Player moved to (1, 0)
Player took 25 damage
Score: 10
Health: 50
Health: 0
Player died
Game Over!
Game running: False
Press any key to exit...
```
- 架构初始化
- 模型 / 系统注册
- 上下文访问
- 旧版命令执行
## 下一步
这个简单的示例展示了 GFramework 的核心概念:
1. **架构模式** - 清晰的分层结构
2. **响应式数据** - BindableProperty 自动更新
3. **事件驱动** - 松耦合的组件通信
4. **命令模式** - 封装用户操作
- 想切到推荐的新请求模型:看 [`../core/cqrs.md`](../core/cqrs.md)
- 想进入游戏层能力:看 [`../game/index.md`](../game/index.md)
- 想看模块入口而不是栏目页:回到对应模块目录下的 `README.md`