Files
Fishing2Server/Fantasy/Fantasy.Net/Fantasy.SourceGenerator/README.md
2025-10-29 17:59:43 +08:00

374 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fantasy.SourceGenerator
Fantasy 框架的增量源代码生成器Incremental Source Generator在编译时自动生成注册代码消除反射开销提升性能并支持 Native AOT。
## 功能特性
### ✅ 已实现的生成器
- **AssemblyInitializerGenerator**: 自动生成程序集初始化器ModuleInitializer在程序集加载时自动注册到框架
- **EntitySystemGenerator**: 自动生成 Entity SystemAwake/Update/Destroy/Deserialize/LateUpdate注册代码
- **EventSystemGenerator**: 自动生成 Event SystemEventSystem/AsyncEventSystem/SphereEventSystem注册代码
- **MessageDispatcherGenerator**: 自动生成消息调度器(网络协议、消息处理器、路由处理器)注册代码
- **EntityTypeCollectionGenerator**: 自动生成 Entity 类型集合,用于框架类型管理
- **ProtoBufGenerator**: 自动生成 ProtoBuf 类型注册代码,用于序列化系统
## 使用方法
### 自动集成(推荐)
Framework 已经完全集成了 Source Generator开发者**无需手动配置**。
#### 工作流程
1. **编译时自动生成**: 当你构建项目时Source Generator 会自动扫描代码并生成注册器
2. **自动注册**: `AssemblyInitializerGenerator` 生成的 `ModuleInitializer` 会在程序集加载时自动注册所有生成器到框架
3. **透明运行**: 框架内部自动使用生成的注册器,无需任何手动调用
#### 示例:添加一个 Entity System
```csharp
// 只需按照 Fantasy 框架的规范编写代码
public class MyEntityAwakeSystem : AwakeSystem<MyEntity>
{
protected override void Awake(MyEntity self)
{
// 你的逻辑
}
}
```
编译后Source Generator 会自动:
- 生成 `EntitySystemRegistrar.g.cs` 包含此 System
- 在程序集加载时自动注册到框架
- 无需任何额外步骤
### 手动集成(高级用途)
如果你需要在自己的项目中引用 Source Generator
```xml
<ItemGroup>
<ProjectReference Include="..\Fantasy.SourceGenerator\Fantasy.SourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
</ItemGroup>
```
确保项目定义了 `FANTASY_NET``FANTASY_UNITY` 预编译符号:
```xml
<PropertyGroup>
<DefineConstants>FANTASY_NET</DefineConstants>
</PropertyGroup>
```
## 生成的代码示例
Source Generator 会在 `obj/.../generated/Fantasy.SourceGenerator/` 目录下自动生成多个注册器类:
### AssemblyInitializer.g.cs核心
```csharp
// 程序集初始化器 - 在程序集加载时自动执行
namespace Fantasy.Generated
{
internal static class AssemblyInitializer
{
[ModuleInitializer] // .NET 使用 ModuleInitializer
// [RuntimeInitializeOnLoadMethod] // Unity 使用此属性
internal static void Initialize()
{
var assembly = typeof(AssemblyInitializer).Assembly;
var assemblyManifestId = HashCodeHelper.ComputeHash64(assembly.GetName().Name);
// 创建所有生成的注册器
var protoBufRegistrar = new Fantasy.Generated.ProtoBufRegistrar();
var eventSystemRegistrar = new Fantasy.Generated.EventSystemRegistrar();
var entitySystemRegistrar = new Fantasy.Generated.EntitySystemRegistrar();
var messageDispatcherRegistrar = new Fantasy.Generated.MessageDispatcherRegistrar();
var entityTypeCollectionRegistrar = new Fantasy.Generated.EntityTypeCollectionRegistrar();
// 一次性注册到框架
Fantasy.Assembly.AssemblyManifest.Register(
assemblyManifestId,
assembly,
protoBufRegistrar,
eventSystemRegistrar,
entitySystemRegistrar,
messageDispatcherRegistrar,
entityTypeCollectionRegistrar);
}
}
// 用于强制加载程序集的标记类
public static class MyAssembly_AssemblyMarker
{
public static void EnsureLoaded() { }
}
}
```
### EntitySystemRegistrar.g.cs
```csharp
namespace Fantasy.Generated
{
internal sealed class EntitySystemRegistrar : IEntitySystemRegistrar
{
public void RegisterSystems(
Dictionary<Type, IAwakeSystem> awakeSystems,
Dictionary<Type, IUpdateSystem> updateSystems,
// ... 其他系统类型
)
{
awakeSystems.Add(typeof(MyEntity), new MyEntityAwakeSystem());
updateSystems.Add(typeof(MyEntity), new MyEntityUpdateSystem());
// ... 自动注册所有发现的 System
}
}
}
```
### EventSystemRegistrar.g.cs
```csharp
namespace Fantasy.Generated
{
internal sealed class EventSystemRegistrar : IEventSystemRegistrar
{
private MyEventHandler _myEventHandler = new MyEventHandler();
public void RegisterSystems(
OneToManyList<Type, IEvent> events,
OneToManyList<Type, IEvent> asyncEvents,
OneToManyList<Type, IEvent> sphereEvents)
{
events.Add(_myEventHandler.EventType(), _myEventHandler);
}
}
}
```
### MessageDispatcherRegistrar.g.cs
```csharp
namespace Fantasy.Generated
{
internal sealed class MessageDispatcherRegistrar : IMessageDispatcherRegistrar
{
public void RegisterSystems(
DoubleMapDictionary<uint, Type> networkProtocols,
Dictionary<Type, Type> responseTypes,
Dictionary<Type, IMessageHandler> messageHandlers,
// ...
)
{
var c2GLoginRequest = new C2G_LoginRequest();
networkProtocols.Add(c2GLoginRequest.OpCode(), typeof(C2G_LoginRequest));
responseTypes.Add(typeof(C2G_LoginRequest), typeof(G2C_LoginResponse));
}
}
}
```
## 性能对比
| 场景 | 反射方式 | Source Generator | 性能提升 |
|------|---------|------------------|---------|
| 程序集加载时注册 | ~50ms100个类型 | ~1ms | **50x** |
| 启动时间 | 受反射影响 | 几乎无影响 | **显著** |
| 运行时实例化 | ~150ns/个 | ~3ns/个 | **50x** |
| 内存分配 | 反射元数据开销 | 无额外开销 | **更优** |
| Native AOT 兼容 | ❌ 不支持 | ✅ 完全支持 | - |
| IL2CPP (Unity) | ⚠️ 性能差 | ✅ 完美支持 | **必需** |
## 调试生成的代码
### 查看生成的代码
生成的代码位于:
```
<项目目录>/obj/<配置>/<目标框架>/generated/Fantasy.SourceGenerator/
```
例如:
```
obj/Debug/net8.0/generated/Fantasy.SourceGenerator/Fantasy.SourceGenerator.Generators.AssemblyInitializerGenerator/AssemblyInitializer.g.cs
obj/Debug/net8.0/generated/Fantasy.SourceGenerator/Fantasy.SourceGenerator.Generators.EntitySystemGenerator/EntitySystemRegistrar.g.cs
```
### IDE 支持
- **Visual Studio**: Dependencies → Analyzers → Fantasy.SourceGenerator → 展开查看生成的文件
- **JetBrains Rider**: Dependencies → Source Generators → Fantasy.SourceGenerator
- **VS Code**: 需要手动浏览 `obj/` 目录
### 启用详细日志
`.csproj` 中添加:
```xml
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)/GeneratedFiles</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
```
这会将生成的文件输出到 `obj/GeneratedFiles/` 目录,方便查看和调试。
## 兼容性
### 平台支持
| 平台 | 状态 | 说明 |
|------|------|------|
| .NET 8.0+ | ✅ 完全支持 | 使用 Roslyn 4.8.0 |
| .NET Framework 4.x | ❌ 不支持 | Source Generator 需要 .NET Standard 2.0+ |
| Unity 2020.2+ | ✅ 完全支持 | 使用 Roslyn 4.0.1(兼容版本) |
| Native AOT | ✅ 完全支持 | 无反射,完美兼容 AOT |
| IL2CPP | ✅ 完全支持 | Unity IL2CPP 必需 |
### 运行模式
- **开发环境**: Source Generator 自动生成,支持热重载(重新编译)
- **生产环境**: Source Generator 自动生成,零反射开销
- **热更新程序集**: 框架会检测可收集的 AssemblyLoadContext支持程序集卸载和重载
## 技术特性
### 增量编译支持
所有生成器都实现了 `IIncrementalGenerator` 接口:
- ✅ 仅在相关代码变化时重新生成
- ✅ 大幅提升编译速度
- ✅ 支持部分代码更新
### 条件编译
生成器会根据预编译符号调整生成代码:
- `FANTASY_NET`: .NET 平台特定功能
- `FANTASY_UNITY`: Unity 平台特定功能
- 自动检测平台并生成对应代码
### 自动清理
- 程序集卸载时自动反注册(支持热更新)
- 实现 `IDisposable` 接口,支持资源释放
- 无内存泄漏风险
## 多版本构建支持
### Unity 版本 vs .NET 版本
此项目支持两种构建配置,使用不同版本的 Roslyn 编译器:
#### Unity 配置(用于 Unity Package
```bash
dotnet build --configuration Unity
```
- **Roslyn 版本**: 4.0.1
- **用途**: Unity 2020.2+ 项目
- **兼容性**: Unity 2020.2 - Unity 2023.x
- **输出**: `bin/Unity/netstandard2.0/Fantasy.SourceGenerator.dll`
#### Release/Debug 配置(用于 .NET 项目)
```bash
dotnet build --configuration Release
```
- **Roslyn 版本**: 4.8.0
- **用途**: .NET 8/9 项目
- **兼容性**: .NET 8.0+
- **输出**: `bin/Release/netstandard2.0/Fantasy.SourceGenerator.dll`
### 为什么需要两个版本?
Unity 使用的 Roslyn 编译器版本较旧Source Generator 引用的 Roslyn 版本必须**小于或等于**运行时编译器版本:
| 平台 | Roslyn 版本 | Source Generator 配置 |
|------|------------|---------------------|
| Unity 2020.2-2021.x | 3.8 - 4.0 | Unity (4.0.1) |
| Unity 2022.x | 4.3 | Unity (4.0.1) |
| Unity 2023.x | 4.6+ | Unity (4.0.1) |
| .NET 8.0 | 4.8+ | Release (4.8.0) |
| .NET 9.0 | 4.10+ | Release (4.8.0) |
### 自动化更新脚本
**更新 Unity Package:**
```bash
# macOS/Linux
./update-unity-source-generator.sh
# Windows
update-unity-source-generator.bat
```
这些脚本会自动使用 Unity 配置构建并更新到 Fantasy.Unity package。在项目的Tools/Update-Unity-Source-Generator下。
### 技术实现
`.csproj` 中使用条件 PackageReference
```xml
<!-- Unity 版本 - Roslyn 4.0.1 -->
<ItemGroup Condition="'$(Configuration)' == 'Unity' OR '$(Configuration)' == 'UnityDebug'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" PrivateAssets="all" />
</ItemGroup>
<!-- .NET 版本 - Roslyn 4.8.0 -->
<ItemGroup Condition="'$(Configuration)' != 'Unity' AND '$(Configuration)' != 'UnityDebug'">
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
```
### 故障排查
**CS9057 警告/错误:**
```
warning CS9057: The analyzer assembly references version 'X' of the compiler,
which is newer than the currently running version 'Y'.
```
**解决方案**: 确保使用 Unity 配置构建用于 Unity 的版本。
## 常见问题FAQ
### Q: 为什么看不到生成的代码?
A: 生成的代码在 `obj/` 目录下,需要编译项目后才能看到。可以在 IDE 的 Analyzers/Source Generators 节点中查看。
### Q: 可以禁用某个生成器吗?
A: 生成器会自动检测代码特征,如果你的项目中没有相关类型,对应的生成器不会生成代码。无需手动禁用。
### Q: Source Generator 会影响编译速度吗?
A: 首次编译会扫描所有代码,后续使用增量编译,仅在相关代码变化时重新生成,对编译速度影响很小。
### Q: Unity 项目如何使用?
A: Fantasy.Unity package 已经包含了兼容版本的 Source GeneratorRoslyn 4.0.1),无需额外配置。
### Q: 支持热重载吗?
A: 支持。框架会检测 AssemblyLoadContext 的卸载事件,自动反注册。重新编译后自动注册新版本。
### Q: Native AOT 部署需要注意什么?
A: 无需特别注意Source Generator 生成的代码不使用反射,完全兼容 Native AOT。
## 贡献指南
欢迎贡献代码!如果你想添加新的生成器:
1.`Generators/` 目录下创建新的生成器类
2. 实现 `IIncrementalGenerator` 接口
3.`AssemblyInitializerGenerator` 中添加对应的注册器接口
4. 更新本 README 文档
---
**维护者**: 初见
**更新日期**: 2025-10-19
**版本**: 1.0.0