339 lines
15 KiB
C#
339 lines
15 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
}
|