框架更新
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user