mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
Merge pull request #250 from GeWuYou/docs/sdk-update-documentation
docs(sdk): 更新文档规范并添加VitePress配置
This commit is contained in:
commit
2f4fccabf2
67
AGENTS.md
67
AGENTS.md
@ -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
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
101
GFramework.Cqrs.Abstractions/README.md
Normal file
101
GFramework.Cqrs.Abstractions/README.md
Normal 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`
|
||||
67
GFramework.Cqrs.SourceGenerators/README.md
Normal file
67
GFramework.Cqrs.SourceGenerators/README.md
Normal 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
154
GFramework.Cqrs/README.md
Normal 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`
|
||||
@ -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
|
||||
|
||||
70
GFramework.Game.SourceGenerators/README.md
Normal file
70
GFramework.Game.SourceGenerators/README.md
Normal 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)
|
||||
@ -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
140
README.md
@ -1,6 +1,6 @@
|
||||
# GFramework
|
||||
|
||||
> 面向游戏开发场景的模块化 C# 框架,核心能力与具体引擎解耦,可按需组合 Core / Game / Godot / Source Generators。
|
||||
> 面向游戏开发场景的模块化 C# 框架,按运行时、抽象层、引擎集成和源码生成器拆分能力。
|
||||
|
||||
[](https://www.nuget.org/packages/GeWuYou.GFramework.Core)
|
||||
[](https://www.nuget.org/packages/GeWuYou.GFramework)
|
||||
@ -8,84 +8,87 @@
|
||||
[](https://dotnet.microsoft.com/)
|
||||
[](LICENSE)
|
||||
[](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)。
|
||||
|
||||
@ -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' },
|
||||
|
||||
20
docs/zh-CN/abstractions/index.md
Normal file
20
docs/zh-CN/abstractions/index.md
Normal 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` 为主。
|
||||
@ -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 和当前代码为准。
|
||||
|
||||
@ -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`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user