Files
Fishing2Server/Fantasy/Fantasy.Net/Fantasy.SourceGenerator/Generators/EntitySystemGenerator.cs
2025-10-29 17:59:43 +08:00

339 lines
15 KiB
C#
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.
using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#pragma warning disable CS8602 // Dereference of a possibly null reference.
namespace Fantasy.SourceGenerator.Generators
{
/// <summary>
/// Entity System 注册代码生成器
/// 自动生成 EntityComponent 所需的 System 注册代码,替代运行时反射
/// </summary>
[Generator]
public partial class EntitySystemGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 查找所有实现了 IEntitySystem 相关接口的类
var systemTypes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: static (node, _) => IsSystemClass(node),
transform: static (ctx, _) => GetSystemTypeInfo(ctx))
.Where(static info => info != null)
.Collect();
// 组合编译信息和找到的类型
var compilationAndTypes = context.CompilationProvider.Combine(systemTypes);
// 注册源代码输出
context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
{
// 检查1: 是否定义了 FANTASY_NET 或 FANTASY_UNITY 预编译符号
if (!CompilationHelper.HasFantasyDefine(source.Left))
{
return;
}
// 检查2: 是否引用了 Fantasy 框架的核心类型
if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.IEntitySystemRegistrar") == null)
{
return;
}
GenerateRegistrationCode(spc, source.Left, source.Right!);
});
}
private static EntitySystemTypeInfo? GetSystemTypeInfo(GeneratorSyntaxContext context)
{
var classDecl = (ClassDeclarationSyntax)context.Node;
if (context.SemanticModel.GetDeclaredSymbol(classDecl) is not INamedTypeSymbol symbol || !symbol.IsInstantiable())
{
return null;
}
var baseType = symbol.BaseType;
if (!baseType.IsGenericType)
{
return null;
}
return baseType.Name switch
{
"AwakeSystem" => EntitySystemTypeInfo.Create(EntitySystemType.AwakeSystem, symbol),
"UpdateSystem" => EntitySystemTypeInfo.Create(EntitySystemType.UpdateSystem, symbol),
"DestroySystem" => EntitySystemTypeInfo.Create(EntitySystemType.DestroySystem, symbol),
"DeserializeSystem" => EntitySystemTypeInfo.Create(EntitySystemType.DeserializeSystem, symbol),
"LateUpdateSystem" => EntitySystemTypeInfo.Create(EntitySystemType.LateUpdateSystem, symbol),
_ => null
};
}
private static void GenerateRegistrationCode(
SourceProductionContext context,
Compilation compilation,
IEnumerable<EntitySystemTypeInfo> systemTypes)
{
var entitySystemTypeInfos = systemTypes.ToList();
// 获取当前程序集名称(仅用于注释)
var assemblyName = compilation.AssemblyName ?? "Unknown";
// 生成代码文件
var builder = new SourceCodeBuilder();
// 添加文件头
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
// 添加 using
builder.AddUsings(
"System",
"System.Collections.Generic",
"Fantasy.Assembly",
"Fantasy.Entitas",
"Fantasy.Entitas.Interface"
);
builder.AppendLine();
// 开始命名空间(固定使用 Fantasy.Generated
builder.BeginNamespace("Fantasy.Generated");
// 开始类定义(实现 IEntitySystemRegistrar 接口)
builder.AddXmlComment($"Auto-generated Entity System registration class for {assemblyName}");
builder.BeginClass("EntitySystemRegistrar", "internal sealed", "IEntitySystemRegistrar");
// 生成字段用于存储 System 实例
GenerateFields(builder, entitySystemTypeInfos);
// 生成注册方法
GenerateRegisterMethod(builder, entitySystemTypeInfos);
// 生成反注册方法
GenerateUnRegisterMethod(builder, entitySystemTypeInfos);
// 结束类和命名空间
builder.EndClass();
builder.EndNamespace();
// 输出源代码
context.AddSource("EntitySystemRegistrar.g.cs", builder.ToString());
}
private static void GenerateFields(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
{
builder.AddComment("Store registered entity system for UnRegister");
if (!entitySystemTypeInfos.Any())
{
return;
}
foreach (var eventSystemTypeInfo in entitySystemTypeInfos)
{
var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
builder.AppendLine($"private {eventSystemTypeInfo.GlobalTypeFullName} {fieldName} = new {eventSystemTypeInfo.GlobalTypeFullName}();");
builder.AppendLine($"private const long _typeHashCode{fieldName} = {eventSystemTypeInfo.EntityTypeHashCode};");
}
builder.AppendLine();
}
private static void GenerateRegisterMethod(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
{
builder.AddXmlComment("Register all Entity Systems to the dictionaries");
builder.AppendLine("#if FANTASY_NET", false);
builder.BeginMethod(
"public void RegisterSystems(" +
"Dictionary<long, Action<Entity>> awakeSystems, " +
"Dictionary<long, Action<Entity>> updateSystems, " +
"Dictionary<long, Action<Entity>> destroySystems, " +
"Dictionary<long, Action<Entity>> deserializeSystems)");
builder.AppendLine("#endif", false);
builder.Unindent();
builder.AppendLine("#if FANTASY_UNITY", false);
builder.BeginMethod(
"public void RegisterSystems(" +
"Dictionary<long, Action<Entity>> awakeSystems, " +
"Dictionary<long, Action<Entity>> updateSystems, " +
"Dictionary<long, Action<Entity>> destroySystems, " +
"Dictionary<long, Action<Entity>> deserializeSystems, " +
"Dictionary<long, Action<Entity>> lateUpdateSystems)");
builder.AppendLine("#endif", false);
if (entitySystemTypeInfos.Any())
{
foreach (var systemTypeInfo in entitySystemTypeInfos)
{
var fieldName = $"_{systemTypeInfo.TypeName.ToCamelCase()}";
switch (systemTypeInfo.EntitySystemType)
{
case EntitySystemType.AwakeSystem:
{
builder.AppendLine($"awakeSystems.Add(_typeHashCode{fieldName}, {fieldName}.Invoke);");
continue;
}
case EntitySystemType.UpdateSystem:
{
builder.AppendLine($"updateSystems.Add(_typeHashCode{fieldName}, {fieldName}.Invoke);");
continue;
}
case EntitySystemType.DestroySystem:
{
builder.AppendLine($"destroySystems.Add(_typeHashCode{fieldName}, {fieldName}.Invoke);");
continue;
}
case EntitySystemType.DeserializeSystem:
{
builder.AppendLine($"deserializeSystems.Add(_typeHashCode{fieldName}, {fieldName}.Invoke);");
continue;
}
case EntitySystemType.LateUpdateSystem:
{
builder.AppendLine("#if FANTASY_UNITY", false);
builder.AppendLine($"awakeSystems.Add(_typeHashCode{fieldName}, {fieldName}.Invoke);");
builder.AppendLine("#endif", false);
continue;
}
}
}
}
builder.EndMethod();
builder.AppendLine();
}
private static void GenerateUnRegisterMethod(SourceCodeBuilder builder, List<EntitySystemTypeInfo> entitySystemTypeInfos)
{
builder.AddXmlComment("Unregister all Entity Systems from the dictionaries");
builder.AppendLine("#if FANTASY_NET", false);
builder.BeginMethod(
"public void UnRegisterSystems(" +
"Dictionary<long, Action<Entity>> awakeSystems, " +
"Dictionary<long, Action<Entity>> updateSystems, " +
"Dictionary<long, Action<Entity>> destroySystems, " +
"Dictionary<long, Action<Entity>> deserializeSystems)");
builder.AppendLine("#endif", false);
builder.Unindent();
builder.AppendLine("#if FANTASY_UNITY", false);
builder.BeginMethod(
"public void UnRegisterSystems(" +
"Dictionary<long, Action<Entity>> awakeSystems, " +
"Dictionary<long, Action<Entity>> updateSystems, " +
"Dictionary<long, Action<Entity>> destroySystems, " +
"Dictionary<long, Action<Entity>> deserializeSystems, " +
"Dictionary<long, Action<Entity>> lateUpdateSystems)");
builder.AppendLine("#endif", false);
if (entitySystemTypeInfos.Any())
{
foreach (var systemTypeInfo in entitySystemTypeInfos)
{
var fieldName = $"_{systemTypeInfo.TypeName.ToCamelCase()}";
switch (systemTypeInfo.EntitySystemType)
{
case EntitySystemType.AwakeSystem:
{
builder.AppendLine($"awakeSystems.Remove(_typeHashCode{fieldName});");
continue;
}
case EntitySystemType.UpdateSystem:
{
builder.AppendLine($"updateSystems.Remove(_typeHashCode{fieldName});");
continue;
}
case EntitySystemType.DestroySystem:
{
builder.AppendLine($"destroySystems.Remove(_typeHashCode{fieldName});");
continue;
}
case EntitySystemType.DeserializeSystem:
{
builder.AppendLine($"deserializeSystems.Remove(_typeHashCode{fieldName});");
continue;
}
case EntitySystemType.LateUpdateSystem:
{
builder.AppendLine("#if FANTASY_UNITY", false);
builder.AppendLine($"lateUpdateSystem.Remove(_typeHashCode{fieldName});");
builder.AppendLine("#endif", false);
continue;
}
}
}
}
builder.EndMethod();
builder.AppendLine();
}
private static bool IsSystemClass(SyntaxNode node)
{
if (node is not ClassDeclarationSyntax classDecl)
{
return false;
}
// 必须有基类型列表(继承抽象类)
if (classDecl.BaseList == null || classDecl.BaseList.Types.Count == 0)
{
return false;
}
// 快速检查是否包含可能的 EntitySystem 基类名称
var baseListText = classDecl.BaseList.ToString();
return baseListText.Contains("AwakeSystem") ||
baseListText.Contains("UpdateSystem") ||
baseListText.Contains("DestroySystem") ||
baseListText.Contains("DeserializeSystem") ||
baseListText.Contains("LateUpdateSystem");
}
private enum EntitySystemType
{
None,
AwakeSystem,
UpdateSystem,
DestroySystem,
DeserializeSystem,
LateUpdateSystem
}
private sealed class EntitySystemTypeInfo
{
public readonly EntitySystemType EntitySystemType;
public readonly string GlobalTypeFullName;
public readonly string TypeFullName;
public readonly string TypeName;
public readonly long EntityTypeHashCode; // 预计算的实体类型哈希值
public readonly string EntityTypeFullName; // 实体类型的完整名称(用于注释)
private EntitySystemTypeInfo(
EntitySystemType entitySystemType,
string globalTypeFullName,
string typeFullName,
string typeName,
long entityTypeHashCode,
string entityTypeFullName)
{
EntitySystemType = entitySystemType;
GlobalTypeFullName = globalTypeFullName;
TypeFullName = typeFullName;
TypeName = typeName;
EntityTypeHashCode = entityTypeHashCode;
EntityTypeFullName = entityTypeFullName;
}
public static EntitySystemTypeInfo Create(EntitySystemType entitySystemType, INamedTypeSymbol symbol)
{
// 获取泛型参数 T (例如AwakeSystem<T> 中的 T)
var entityType = symbol.BaseType?.TypeArguments.FirstOrDefault();
var entityTypeFullName = entityType?.GetFullName(false) ?? "Unknown";
// 使用与运行时相同的算法计算哈希值
var entityTypeHashCode = HashCodeHelper.ComputeHash64(entityTypeFullName);
return new EntitySystemTypeInfo(
entitySystemType,
symbol.GetFullName(),
symbol.GetFullName(false),
symbol.Name,
entityTypeHashCode,
entityTypeFullName);
}
}
}
}