框架更新
This commit is contained in:
@@ -0,0 +1,259 @@
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集初始化器生成器
|
||||
/// 为每个 Fantasy 项目生成 ModuleInitializer,在程序集加载时自动注册到框架
|
||||
/// 支持 Native AOT,无需运行时反射
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public class AssemblyInitializerGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 获取编译信息
|
||||
var compilationProvider = context.CompilationProvider;
|
||||
// 注册源代码输出
|
||||
context.RegisterSourceOutput(compilationProvider, (spc, compilation) =>
|
||||
{
|
||||
GenerateModuleInitializer(spc, compilation);
|
||||
});
|
||||
}
|
||||
|
||||
private static void GenerateModuleInitializer(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation)
|
||||
{
|
||||
// 检查是否定义了 FANTASY_NET 或 FANTASY_UNITY 预编译符号
|
||||
if (!CompilationHelper.HasFantasyDefine(compilation))
|
||||
{
|
||||
// 不是 Fantasy 框架项目,跳过代码生成
|
||||
return;
|
||||
}
|
||||
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
// 检测是否是 Unity 环境
|
||||
var isUnity = CompilationHelper.IsUnityCompilation(compilation);
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"UnityEngine"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Runtime.CompilerServices"
|
||||
);
|
||||
}
|
||||
builder.AppendLine();
|
||||
// 开始命名空间
|
||||
builder.BeginNamespace(GeneratorConstants.GeneratedNamespace);
|
||||
// 添加类注释
|
||||
builder.AddXmlComment($"Auto-generated assembly initializer for {assemblyName}");
|
||||
builder.AddXmlComment("This class is automatically invoked when the assembly is loaded via ModuleInitializer");
|
||||
// 开始类定义
|
||||
builder.BeginClass("AssemblyInitializer", "internal static");
|
||||
// 添加字段
|
||||
builder.AppendLine("private static bool _initialized;");
|
||||
builder.AppendLine("private static long _assemblyManifestId;");
|
||||
builder.AppendLine();
|
||||
|
||||
// 生成 ModuleInitializer 方法
|
||||
GenerateInitializeMethod(builder, assemblyName, isUnity);
|
||||
|
||||
// 生成卸载方法
|
||||
GenerateUnloadMethod(builder, isUnity);
|
||||
|
||||
// 结束 AssemblyInitializer 类
|
||||
builder.EndClass();
|
||||
|
||||
builder.AppendLine();
|
||||
|
||||
// 生成 AssemblyMarker 类(用于强制加载程序集)
|
||||
// 类名包含程序集名称以避免冲突,将特殊字符替换为下划线
|
||||
var markerClassName = assemblyName.Replace("-", "_").Replace(".", "_") + "_AssemblyMarker";
|
||||
builder.AddXmlComment($"Public marker class for forcing {assemblyName} assembly load");
|
||||
builder.AddXmlComment("Access this type to ensure the assembly is loaded and ModuleInitializer executes");
|
||||
builder.BeginClass(markerClassName, "public static");
|
||||
builder.AppendLine("/// <summary>");
|
||||
builder.AppendLine("/// Call this method to ensure the assembly is loaded");
|
||||
builder.AppendLine("/// This is useful when loading assemblies dynamically via reflection");
|
||||
builder.AppendLine("/// </summary>");
|
||||
builder.BeginMethod("public static void EnsureLoaded()");
|
||||
builder.AppendLine("// Accessing this method ensures the assembly is loaded");
|
||||
builder.AppendLine("// ModuleInitializer will execute automatically when assembly loads");
|
||||
builder.EndMethod();
|
||||
builder.EndClass();
|
||||
|
||||
// 结束命名空间
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("AssemblyInitializer.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static void GenerateInitializeMethod(SourceCodeBuilder builder, string assemblyName, bool isUnity)
|
||||
{
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AddXmlComment(
|
||||
"Unity runtime initializer - automatically called when entering play mode or on app start");
|
||||
builder.AppendLine("[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]");
|
||||
builder.BeginMethod("internal static void Initialize()");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddXmlComment("Module initializer - automatically called when assembly is loaded");
|
||||
builder.AppendLine("[ModuleInitializer]");
|
||||
builder.BeginMethod("internal static void Initialize()");
|
||||
}
|
||||
|
||||
// 防止重复初始化
|
||||
builder.AppendLine("if (_initialized)");
|
||||
builder.OpenBrace();
|
||||
builder.AppendLine("return;");
|
||||
builder.CloseBrace();
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("_initialized = true;");
|
||||
builder.AppendLine();
|
||||
|
||||
// 获取程序集并计算唯一标识
|
||||
builder.AddComment("Get assembly and calculate manifest ID");
|
||||
builder.AppendLine("var assembly = typeof(AssemblyInitializer).Assembly;");
|
||||
builder.AppendLine(
|
||||
$"_assemblyManifestId = Fantasy.Helper.HashCodeHelper.ComputeHash64(assembly.GetName().Name ?? \"{assemblyName}\");");
|
||||
builder.AppendLine();
|
||||
|
||||
// 注册卸载事件(用于热更新支持)
|
||||
builder.AddComment("Register auto-unload for collectible AssemblyLoadContext (hot-reload support)");
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AppendLine(
|
||||
"#if !UNITY_EDITOR && !UNITY_STANDALONE && !UNITY_ANDROID && !UNITY_IOS && !UNITY_WEBGL");
|
||||
}
|
||||
|
||||
builder.AppendLine("var loadContext = System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(assembly);");
|
||||
builder.AppendLine(
|
||||
"if (loadContext != null && loadContext != System.Runtime.Loader.AssemblyLoadContext.Default)");
|
||||
builder.OpenBrace();
|
||||
builder.AppendLine("loadContext.Unloading += OnAssemblyUnloading;");
|
||||
builder.CloseBrace();
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AppendLine("#endif");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
|
||||
// 声明所有可能的注册器变量
|
||||
builder.AddComment("Declare registrar variables");
|
||||
builder.AppendLine("Fantasy.Assembly.INetworkProtocolRegistrar? networkProtocolRegistrar = null;");
|
||||
builder.AppendLine("Fantasy.Assembly.IEventSystemRegistrar? eventSystemRegistrar = null;");
|
||||
builder.AppendLine("Fantasy.Assembly.IEntitySystemRegistrar? entitySystemRegistrar = null;");
|
||||
builder.AppendLine("Fantasy.Assembly.IMessageHandlerResolver? messageHandlerResolverRegistrar = null;");
|
||||
builder.AppendLine(
|
||||
"Fantasy.Assembly.IEntityTypeCollectionRegistrar? entityTypeCollectionRegistrar = null;");
|
||||
builder.AppendLine(
|
||||
"Fantasy.Assembly.INetworkProtocolOpCodeResolver? networkProtocolOpCodeResolverRegistrar = null;");
|
||||
builder.AppendLine(
|
||||
"Fantasy.Assembly.INetworkProtocolResponseTypeResolver? networkProtocolResponseTypeResolverRegistrar = null;");
|
||||
builder.AppendLine("#if FANTASY_NET", false);
|
||||
builder.AppendLine(
|
||||
"Fantasy.Assembly.ISeparateTableRegistrar? separateTableRegistrar = null;");
|
||||
builder.AppendLine("#endif", false);
|
||||
builder.AppendLine();
|
||||
|
||||
// 尝试创建各个注册器(如果存在)
|
||||
builder.AddComment("Try to create registrars if they were generated in this assembly");
|
||||
GenerateTryCreateRegistrar(builder, "NetworkProtocol", "networkProtocolRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "EventSystem", "eventSystemRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "EntitySystem", "entitySystemRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "MessageHandlerResolver", "messageHandlerResolverRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "EntityTypeCollection", "entityTypeCollectionRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "NetworkProtocolOpCodeResolver", "networkProtocolOpCodeResolverRegistrar");
|
||||
GenerateTryCreateRegistrar(builder, "NetworkProtocolResponseTypeResolver", "networkProtocolResponseTypeResolverRegistrar");
|
||||
builder.AppendLine("#if FANTASY_NET", false);
|
||||
GenerateTryCreateRegistrar(builder, "SeparateTable", "separateTableRegistrar");
|
||||
builder.AppendLine("#endif", false);
|
||||
|
||||
builder.AppendLine();
|
||||
|
||||
// 注册到框架
|
||||
builder.AddComment("Register complete AssemblyManifest to the framework");
|
||||
builder.AppendLine("#if FANTASY_NET", false);
|
||||
builder.AppendLine("Fantasy.Assembly.AssemblyManifest.Register(");
|
||||
builder.Indent();
|
||||
builder.AppendLine("_assemblyManifestId,");
|
||||
builder.AppendLine("assembly,");
|
||||
builder.AppendLine("networkProtocolRegistrar,");
|
||||
builder.AppendLine("eventSystemRegistrar,");
|
||||
builder.AppendLine("entitySystemRegistrar,");
|
||||
builder.AppendLine("messageHandlerResolverRegistrar,");
|
||||
builder.AppendLine("entityTypeCollectionRegistrar,");
|
||||
builder.AppendLine("separateTableRegistrar,");
|
||||
builder.AppendLine("networkProtocolOpCodeResolverRegistrar,");
|
||||
builder.AppendLine("networkProtocolResponseTypeResolverRegistrar);");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("#endif", false);
|
||||
builder.AppendLine("#if FANTASY_UNITY", false);
|
||||
builder.AppendLine("Fantasy.Assembly.AssemblyManifest.Register(");
|
||||
builder.Indent();
|
||||
builder.AppendLine("_assemblyManifestId,");
|
||||
builder.AppendLine("assembly,");
|
||||
builder.AppendLine("networkProtocolRegistrar,");
|
||||
builder.AppendLine("eventSystemRegistrar,");
|
||||
builder.AppendLine("entitySystemRegistrar,");
|
||||
builder.AppendLine("messageHandlerResolverRegistrar,");
|
||||
builder.AppendLine("entityTypeCollectionRegistrar,");
|
||||
builder.AppendLine("networkProtocolOpCodeResolverRegistrar,");
|
||||
builder.AppendLine("networkProtocolResponseTypeResolverRegistrar);");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("#endif", false);
|
||||
builder.EndMethod();
|
||||
}
|
||||
|
||||
private static void GenerateUnloadMethod(SourceCodeBuilder builder, bool isUnity)
|
||||
{
|
||||
builder.AppendLine();
|
||||
builder.AddXmlComment("Called when AssemblyLoadContext is unloading (for hot-reload support)");
|
||||
|
||||
// Unity 环境下,AssemblyLoadContext 仅在非编辑器/非独立平台可用
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AppendLine("#if !UNITY_EDITOR && !UNITY_STANDALONE && !UNITY_ANDROID && !UNITY_IOS && !UNITY_WEBGL");
|
||||
}
|
||||
|
||||
builder.BeginMethod("private static void OnAssemblyUnloading(System.Runtime.Loader.AssemblyLoadContext context)");
|
||||
|
||||
builder.AddComment("Unregister from framework");
|
||||
builder.AppendLine("if (_assemblyManifestId != 0)");
|
||||
builder.OpenBrace();
|
||||
builder.AppendLine("Fantasy.Assembly.AssemblyManifest.Unregister(_assemblyManifestId);");
|
||||
builder.CloseBrace();
|
||||
|
||||
builder.EndMethod();
|
||||
|
||||
if (isUnity)
|
||||
{
|
||||
builder.AppendLine("#endif");
|
||||
}
|
||||
}
|
||||
|
||||
private static void GenerateTryCreateRegistrar(
|
||||
SourceCodeBuilder builder,
|
||||
string registrarName,
|
||||
string variableName)
|
||||
{
|
||||
var typeName = $"Fantasy.Generated.{registrarName}Registrar";
|
||||
builder.AppendLine($"{variableName} = new {typeName}();");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators
|
||||
{
|
||||
[Generator]
|
||||
public partial class EntityTypeCollectionGenerate : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 查找所有实现了 EventSystem 相关抽象类的类
|
||||
var protoBufTypes = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsEntityClass(node),
|
||||
transform: static (ctx, _) => GetEntityTypeInfo(ctx))
|
||||
.Where(static info => info != null)
|
||||
.Collect();
|
||||
// 组合编译信息和找到的类型
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(protoBufTypes);
|
||||
// 注册源代码输出
|
||||
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.IEntityTypeCollectionRegistrar") == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateRegistrationCode(spc, source.Left, source.Right!);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成注册代码
|
||||
/// </summary>
|
||||
private static void GenerateRegistrationCode(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation,
|
||||
IEnumerable<EntityTypeInfo> entityTypeInfos)
|
||||
{
|
||||
var entityTypeList = entityTypeInfos.ToList();
|
||||
// 获取当前程序集名称(仅用于注释)
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Collection",
|
||||
"Fantasy.Event"
|
||||
);
|
||||
builder.AppendLine();
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 IEntityTypeCollectionRegistrar 接口)
|
||||
builder.AddXmlComment($"Automatically generated Entity Type collection class for {assemblyName}");
|
||||
builder.BeginClass("EntityTypeCollectionRegistrar", "internal sealed", "IEntityTypeCollectionRegistrar");
|
||||
// 生成 GetEntityTypes 方法
|
||||
GetEntityTypesMethod(builder, entityTypeList);
|
||||
builder.AppendLine();
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("EntityTypeCollectionRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static void GetEntityTypesMethod(SourceCodeBuilder builder, List<EntityTypeInfo> entityTypeInfos)
|
||||
{
|
||||
builder.AddXmlComment("All Entity Types");
|
||||
builder.BeginMethod("public List<Type> GetEntityTypes()");
|
||||
|
||||
if (entityTypeInfos.Any())
|
||||
{
|
||||
builder.AppendLine($"return new List<Type>({entityTypeInfos.Count})");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var system in entityTypeInfos)
|
||||
{
|
||||
builder.AppendLine($"typeof({system.EntityTypeFullName}),");
|
||||
}
|
||||
builder.Unindent();
|
||||
builder.AppendLine("};");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return new List<Type>();");
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
}
|
||||
|
||||
private static bool IsEntityClass(SyntaxNode node)
|
||||
{
|
||||
if (node is not ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 必须有基类型列表(实现接口)
|
||||
return classDecl.BaseList != null && classDecl.BaseList.Types.Any();
|
||||
}
|
||||
|
||||
private static EntityTypeInfo? GetEntityTypeInfo(GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
var semanticModel = context.SemanticModel;
|
||||
|
||||
// 检查是否继承自 Entity 或 Entity<T>
|
||||
if (!InheritsFromEntity(classDecl, semanticModel))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
|
||||
|
||||
if (symbol == null || !symbol.IsInstantiable())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new EntityTypeInfo(
|
||||
symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat),
|
||||
symbol.Name
|
||||
);
|
||||
}
|
||||
|
||||
private static bool InheritsFromEntity(ClassDeclarationSyntax classDecl, SemanticModel semanticModel)
|
||||
{
|
||||
if (classDecl.BaseList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var baseType in classDecl.BaseList.Types)
|
||||
{
|
||||
var typeInfo = semanticModel.GetTypeInfo(baseType.Type);
|
||||
var baseTypeSymbol = typeInfo.Type as INamedTypeSymbol;
|
||||
|
||||
if (baseTypeSymbol == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查是否是 Entity(非泛型)
|
||||
if (baseTypeSymbol.Name == "Entity" && baseTypeSymbol.Arity == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查是否是 Entity<T>(泛型)
|
||||
if (baseTypeSymbol.IsGenericType)
|
||||
{
|
||||
var originalDef = baseTypeSymbol.OriginalDefinition;
|
||||
if (originalDef.Name == "Entity" && originalDef.Arity == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sealed record EntityTypeInfo(
|
||||
string EntityTypeFullName,
|
||||
string EntityTypeName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
// ReSharper disable AssignNullToNotNullAttribute
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// Event System 注册代码生成器
|
||||
/// 自动生成 EventComponent 所需的 Event System 注册代码,替代运行时反射
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public partial class EventSystemGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 查找所有实现了 EventSystem 相关抽象类的类
|
||||
var eventSystemTypes = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsEventSystemClass(node),
|
||||
transform: static (ctx, _) => GetEventSystemTypeInfo(ctx))
|
||||
.Where(static info => info != null)
|
||||
.Collect();
|
||||
// 组合编译信息和找到的类型
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(eventSystemTypes);
|
||||
// 注册源代码输出
|
||||
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.IEventSystemRegistrar") == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateRegistrationCode(spc, source.Left, source.Right!);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提取 EventSystem 类型信息
|
||||
/// </summary>
|
||||
private static EventSystemTypeInfo? GetEventSystemTypeInfo(GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDecl) is not INamedTypeSymbol symbol || !symbol.IsInstantiable())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (symbol.BaseType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var baseType = symbol.BaseType;
|
||||
|
||||
if (!baseType.IsGenericType)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return baseType.Name switch
|
||||
{
|
||||
"EventSystem" => EventSystemTypeInfo.Create(EventSystemType.EventSystem, symbol),
|
||||
"AsyncEventSystem" => EventSystemTypeInfo.Create(EventSystemType.AsyncEventSystem, symbol),
|
||||
"SphereEventSystem" => EventSystemTypeInfo.Create(EventSystemType.SphereEventSystem, symbol),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成注册代码
|
||||
/// </summary>
|
||||
private static void GenerateRegistrationCode(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation,
|
||||
IEnumerable<EventSystemTypeInfo> eventSystemTypes)
|
||||
{
|
||||
var eventSystems = eventSystemTypes.ToList();
|
||||
// 获取当前程序集名称(仅用于注释)
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
// 生成代码文件
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Collection",
|
||||
"Fantasy.Event"
|
||||
);
|
||||
builder.AppendLine();
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 IEventSystemRegistrar 接口)
|
||||
builder.AddXmlComment($"Auto-generated Event System registration class for {assemblyName}");
|
||||
builder.BeginClass("EventSystemRegistrar", "internal sealed", "IEventSystemRegistrar");
|
||||
// 生成字段用于存储已注册的事件处理器(用于 UnRegister)
|
||||
GenerateFields(builder, eventSystems);
|
||||
// 生成 RegisterSystems 方法
|
||||
GenerateRegisterMethod(builder, eventSystems);
|
||||
// 生成 UnRegisterSystems 方法
|
||||
GenerateUnRegisterMethod(builder, eventSystems);
|
||||
// 生成 Dispose 方法
|
||||
GenerateDisposeMethod(builder, eventSystems);
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("EventSystemRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成字段,用于存储已注册的事件处理器实例
|
||||
/// </summary>
|
||||
private static void GenerateFields(SourceCodeBuilder builder, List<EventSystemTypeInfo> eventSystems)
|
||||
{
|
||||
builder.AddComment("Store registered event handlers for UnRegister");
|
||||
|
||||
if (!eventSystems.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var eventSystemTypeInfo in eventSystems)
|
||||
{
|
||||
var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
|
||||
builder.AppendLine($"private {eventSystemTypeInfo.TypeFullName} {fieldName} = new {eventSystemTypeInfo.TypeFullName}();");
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 RegisterSystems 方法
|
||||
/// </summary>
|
||||
private static void GenerateRegisterMethod(SourceCodeBuilder builder, List<EventSystemTypeInfo> eventSystems)
|
||||
{
|
||||
builder.AddXmlComment("Register all Event Systems to the containers");
|
||||
builder.BeginMethod(
|
||||
"public void RegisterSystems(" +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> events, " +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents, " +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents)");
|
||||
|
||||
if (eventSystems.Any())
|
||||
{
|
||||
foreach (var eventSystemTypeInfo in eventSystems)
|
||||
{
|
||||
var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
|
||||
|
||||
switch (eventSystemTypeInfo.EventSystemType)
|
||||
{
|
||||
case EventSystemType.EventSystem:
|
||||
{
|
||||
builder.AppendLine($"events.Add({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
case EventSystemType.AsyncEventSystem:
|
||||
{
|
||||
builder.AppendLine($"asyncEvents.Add({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
case EventSystemType.SphereEventSystem:
|
||||
{
|
||||
builder.AppendLine($"sphereEvents.Add({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 UnRegisterSystems 方法
|
||||
/// </summary>
|
||||
private static void GenerateUnRegisterMethod(SourceCodeBuilder builder, List<EventSystemTypeInfo> eventSystems)
|
||||
{
|
||||
builder.AddXmlComment("Unregister all Event Systems from the containers (called on hot reload)");
|
||||
builder.BeginMethod(
|
||||
"public void UnRegisterSystems(" +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> events, " +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> asyncEvents, " +
|
||||
"OneToManyList<RuntimeTypeHandle, IEvent> sphereEvents)");
|
||||
|
||||
if (eventSystems.Any())
|
||||
{
|
||||
foreach (var eventSystemTypeInfo in eventSystems)
|
||||
{
|
||||
var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
|
||||
|
||||
switch (eventSystemTypeInfo.EventSystemType)
|
||||
{
|
||||
case EventSystemType.EventSystem:
|
||||
{
|
||||
builder.AppendLine($"events.RemoveValue({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
case EventSystemType.AsyncEventSystem:
|
||||
{
|
||||
builder.AppendLine($"asyncEvents.RemoveValue({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
case EventSystemType.SphereEventSystem:
|
||||
{
|
||||
builder.AppendLine($"sphereEvents.RemoveValue({fieldName}.EventType().TypeHandle, {fieldName});");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成 Dispose 方法
|
||||
/// </summary>
|
||||
private static void GenerateDisposeMethod(SourceCodeBuilder builder, List<EventSystemTypeInfo> eventSystems)
|
||||
{
|
||||
builder.AddXmlComment("Dispose all resources");
|
||||
builder.BeginMethod("public void Dispose()");
|
||||
builder.AddComment("Clear all references");
|
||||
|
||||
if (eventSystems.Any())
|
||||
{
|
||||
foreach (var eventSystemTypeInfo in eventSystems)
|
||||
{
|
||||
var fieldName = $"_{eventSystemTypeInfo.TypeName.ToCamelCase()}";
|
||||
builder.AppendLine($"{fieldName} = null;");
|
||||
}
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 快速判断语法节点是否可能是 EventSystem 类
|
||||
/// </summary>
|
||||
private static bool IsEventSystemClass(SyntaxNode node)
|
||||
{
|
||||
if (node is not ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 必须有基类型列表(继承抽象类)
|
||||
if (classDecl.BaseList == null || classDecl.BaseList.Types.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 快速检查是否包含可能的 EventSystem 基类名称
|
||||
var baseListText = classDecl.BaseList.ToString();
|
||||
return baseListText.Contains("AwakeSystem") ||
|
||||
baseListText.Contains("AsyncEventSystem") ||
|
||||
baseListText.Contains("SphereEventSystem");
|
||||
}
|
||||
|
||||
private enum EventSystemType
|
||||
{
|
||||
None,
|
||||
EventSystem, // EventSystem<T> 事件类
|
||||
AsyncEventSystem, // AsyncEventSystem<T> 事件类
|
||||
SphereEventSystem, // SphereEventSystem<T> 事件类
|
||||
}
|
||||
|
||||
private sealed class EventSystemTypeInfo
|
||||
{
|
||||
public readonly EventSystemType EventSystemType;
|
||||
public readonly string TypeFullName;
|
||||
public readonly string TypeName;
|
||||
|
||||
private EventSystemTypeInfo(EventSystemType eventSystemType, string typeFullName, string typeName)
|
||||
{
|
||||
EventSystemType = eventSystemType;
|
||||
TypeFullName = typeFullName;
|
||||
TypeName = typeName;
|
||||
}
|
||||
|
||||
public static EventSystemTypeInfo Create(EventSystemType eventSystemType, INamedTypeSymbol symbol)
|
||||
{
|
||||
return new EventSystemTypeInfo(
|
||||
eventSystemType,
|
||||
symbol.GetFullName(),
|
||||
symbol.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators
|
||||
{
|
||||
[Generator]
|
||||
public sealed class MessageHandlerGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 查找所有实现了消息相关接口的类
|
||||
var messageTypes = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsMessageHandlerClass(node),
|
||||
transform: static (ctx, _) => GetMessageTypeInfo(ctx))
|
||||
.Where(static info => info != null)
|
||||
.Collect();
|
||||
// 组合编译信息和找到的类型
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(messageTypes);
|
||||
// 注册源代码输出
|
||||
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.INetworkProtocolRegistrar") == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateRegistrationCode(spc, source.Left, source.Right!);
|
||||
});
|
||||
}
|
||||
|
||||
private static void GenerateRegistrationCode(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation,
|
||||
IEnumerable<MessageHandlerInfo> messageHandlerInfos)
|
||||
{
|
||||
var messageHandlers = new List<MessageHandlerInfo>();
|
||||
var routeMessageHandlers = new List<MessageHandlerInfo>();
|
||||
|
||||
foreach (var messageHandlerInfo in messageHandlerInfos)
|
||||
{
|
||||
switch (messageHandlerInfo.HandlerType)
|
||||
{
|
||||
case HandlerType.MessageHandler:
|
||||
{
|
||||
messageHandlers.Add(messageHandlerInfo);
|
||||
break;
|
||||
}
|
||||
case HandlerType.RouteMessageHandler:
|
||||
{
|
||||
routeMessageHandlers.Add(messageHandlerInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
var builder = new SourceCodeBuilder();
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Dictionary",
|
||||
"Fantasy.Network.Interface",
|
||||
"Fantasy.Network",
|
||||
"Fantasy.Entitas",
|
||||
"Fantasy.Async",
|
||||
"System.Runtime.CompilerServices"
|
||||
);
|
||||
builder.AppendLine();
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
builder.AddXmlComment($"Auto-generated message handler registration class for {assemblyName}");
|
||||
builder.BeginClass("MessageHandlerResolverRegistrar", "internal sealed", "IMessageHandlerResolver");
|
||||
// 生成字段用于存储已注册的实例(用于 UnRegister)
|
||||
GenerateFields(builder, messageHandlers, routeMessageHandlers);
|
||||
// 生成 Register 方法
|
||||
GenerateRegistrationCode(builder, messageHandlers, routeMessageHandlers);
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("MessageHandlerResolverRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static void GenerateFields(SourceCodeBuilder builder, List<MessageHandlerInfo> messageHandlers, List<MessageHandlerInfo> routeMessageHandlers)
|
||||
{
|
||||
foreach (var messageHandlerInfo in messageHandlers)
|
||||
{
|
||||
builder.AppendLine($"private Func<Session, uint, uint, object, FTask> message_{messageHandlerInfo.TypeName} = new {messageHandlerInfo.TypeFullName}().Handle;");
|
||||
}
|
||||
|
||||
foreach (var messageHandlerInfo in routeMessageHandlers)
|
||||
{
|
||||
builder.AppendLine($"private Func<Session, Entity, uint, object, FTask> routeMessage_{messageHandlerInfo.TypeName} = new {messageHandlerInfo.TypeFullName}().Handle;");
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void GenerateRegistrationCode(SourceCodeBuilder builder, List<MessageHandlerInfo> messageHandlers, List<MessageHandlerInfo> routeMessageHandlers)
|
||||
{
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int GetMessageHandlerCount()");
|
||||
builder.AppendLine($"return {messageHandlers.Count};");
|
||||
builder.EndMethod();
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int GetRouteMessageHandlerCount()");
|
||||
builder.AppendLine($"return {routeMessageHandlers.Count};");
|
||||
builder.EndMethod();
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public bool MessageHandler(Session session, uint rpcId, uint protocolCode, object message)");
|
||||
if (messageHandlers.Any())
|
||||
{
|
||||
builder.AppendLine("switch (protocolCode)");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var messageHandlerInfo in messageHandlers)
|
||||
{
|
||||
builder.AppendLine($"case {messageHandlerInfo.OpCode}:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"message_{messageHandlerInfo.TypeName}(session, rpcId, protocolCode, message).Coroutine();");
|
||||
builder.AppendLine($"return true;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
builder.AppendLine("default:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return false;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return false;");
|
||||
}
|
||||
builder.EndMethod();
|
||||
|
||||
builder.AppendLine("#if FANTASY_NET", false);
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public async FTask<bool> RouteMessageHandler(Session session, Entity entity, uint rpcId, uint protocolCode, object message)");
|
||||
if (routeMessageHandlers.Any())
|
||||
{
|
||||
builder.AppendLine("switch (protocolCode)");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var routeMessageHandler in routeMessageHandlers)
|
||||
{
|
||||
builder.AppendLine($"case {routeMessageHandler.OpCode}:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"await routeMessage_{routeMessageHandler.TypeName}(session, entity, rpcId, message);");
|
||||
builder.AppendLine($"return true;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
builder.AppendLine("default:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"await FTask.CompletedTask;");
|
||||
builder.AppendLine($"return false;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"await FTask.CompletedTask;");
|
||||
builder.AppendLine($"return false;");
|
||||
}
|
||||
builder.EndMethod();
|
||||
builder.AppendLine("#endif", false);
|
||||
}
|
||||
|
||||
private static bool IsMessageHandlerClass(SyntaxNode node)
|
||||
{
|
||||
if (node is not ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (classDecl.BaseList == null || !classDecl.BaseList.Types.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var baseType in classDecl.BaseList.Types)
|
||||
{
|
||||
var typeName = baseType.Type.ToString();
|
||||
|
||||
if (typeName.Contains("IMessageHandler") ||
|
||||
typeName.Contains("IRouteMessageHandler") ||
|
||||
typeName.Contains("Message<") ||
|
||||
typeName.Contains("MessageRPC<") ||
|
||||
typeName.Contains("Route<") ||
|
||||
typeName.Contains("RouteRPC<") ||
|
||||
typeName.Contains("Addressable<") ||
|
||||
typeName.Contains("AddressableRPC<") ||
|
||||
typeName.Contains("Roaming<") ||
|
||||
typeName.Contains("RoamingRPC<"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static MessageHandlerInfo? GetMessageTypeInfo(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 is not { IsGenericType: true } || baseType.TypeArguments.Length <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var baseTypeName = baseType.OriginalDefinition.ToDisplayString();
|
||||
|
||||
switch (baseTypeName)
|
||||
{
|
||||
case "Fantasy.Network.Interface.Message<T>":
|
||||
case "Fantasy.Network.Interface.MessageRPC<TRequest, TResponse>":
|
||||
{
|
||||
return new MessageHandlerInfo(
|
||||
HandlerType.MessageHandler,
|
||||
symbol.GetFullName(),
|
||||
symbol.Name,
|
||||
GetOpCode(context, baseType, 0));
|
||||
}
|
||||
case "Fantasy.Network.Interface.Route<TEntity, TMessage>":
|
||||
case "Fantasy.Network.Interface.RouteRPC<TEntity, TRouteRequest, TRouteResponse>":
|
||||
case "Fantasy.Network.Interface.Addressable<TEntity, TMessage>":
|
||||
case "Fantasy.Network.Interface.AddressableRPC<TEntity, TRouteRequest, TRouteResponse>":
|
||||
case "Fantasy.Network.Interface.Roaming<TEntity, TMessage>":
|
||||
case "Fantasy.Network.Interface.RoamingRPC<TEntity, TRouteRequest, TRouteResponse>":
|
||||
{
|
||||
return new MessageHandlerInfo(
|
||||
HandlerType.RouteMessageHandler,
|
||||
symbol.GetFullName(),
|
||||
symbol.Name,
|
||||
GetOpCode(context, baseType, 1));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static uint? GetOpCode(GeneratorSyntaxContext context, INamedTypeSymbol baseType, int index)
|
||||
{
|
||||
if (baseType.TypeArguments.Length <= index)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var messageType = (INamedTypeSymbol)baseType.TypeArguments[index];
|
||||
var messageName = messageType.Name;
|
||||
var compilation = context.SemanticModel.Compilation;
|
||||
|
||||
// 策略1:从消息类型所在程序集中搜索 OpCode 类
|
||||
var messageAssembly = messageType.ContainingAssembly;
|
||||
var namespaceName = messageType.ContainingNamespace.ToDisplayString();
|
||||
|
||||
// 遍历程序集中的所有类型,查找 OuterOpcode 或 InnerOpcode
|
||||
var opCodeTypeNames = new[] { "OuterOpcode", "InnerOpcode" };
|
||||
foreach (var opCodeTypeName in opCodeTypeNames)
|
||||
{
|
||||
var opCodeType = FindTypeInAssembly(messageAssembly.GlobalNamespace, namespaceName, opCodeTypeName);
|
||||
if (opCodeType != null)
|
||||
{
|
||||
var opCodeField = opCodeType.GetMembers(messageName).OfType<IFieldSymbol>().FirstOrDefault();
|
||||
if (opCodeField != null && opCodeField.IsConst && opCodeField.ConstantValue is uint constValue)
|
||||
{
|
||||
return constValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 策略2:如果策略1失败,尝试从 OpCode() 方法的语法树中解析(仅适用于同项目中的消息)
|
||||
var opCodeMethod = messageType.GetMembers("OpCode").OfType<IMethodSymbol>().FirstOrDefault();
|
||||
if (opCodeMethod != null)
|
||||
{
|
||||
var opCodeSyntax = opCodeMethod.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MethodDeclarationSyntax;
|
||||
if (opCodeSyntax?.Body != null)
|
||||
{
|
||||
var returnStatement = opCodeSyntax.Body.DescendantNodes()
|
||||
.OfType<ReturnStatementSyntax>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (returnStatement?.Expression != null)
|
||||
{
|
||||
var syntaxTree = opCodeSyntax.SyntaxTree;
|
||||
|
||||
if (compilation.ContainsSyntaxTree(syntaxTree))
|
||||
{
|
||||
var semanticModel = compilation.GetSemanticModel(syntaxTree);
|
||||
|
||||
// 尝试符号解析
|
||||
var symbolInfo = semanticModel.GetSymbolInfo(returnStatement.Expression);
|
||||
if (symbolInfo.Symbol is IFieldSymbol fieldSymbol && fieldSymbol.IsConst && fieldSymbol.ConstantValue is uint constValue2)
|
||||
{
|
||||
return constValue2;
|
||||
}
|
||||
|
||||
// 尝试常量值解析
|
||||
var constantValue = semanticModel.GetConstantValue(returnStatement.Expression);
|
||||
if (constantValue.HasValue && constantValue.Value is uint uintValue)
|
||||
{
|
||||
return uintValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 辅助方法:在程序集的命名空间中递归查找指定类型
|
||||
private static INamedTypeSymbol? FindTypeInAssembly(INamespaceSymbol namespaceSymbol, string targetNamespace, string typeName)
|
||||
{
|
||||
// 如果当前命名空间匹配目标命名空间,查找类型
|
||||
if (namespaceSymbol.ToDisplayString() == targetNamespace)
|
||||
{
|
||||
var type = namespaceSymbol.GetTypeMembers(typeName).FirstOrDefault();
|
||||
if (type != null)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归搜索子命名空间
|
||||
foreach (var childNamespace in namespaceSymbol.GetNamespaceMembers())
|
||||
{
|
||||
var result = FindTypeInAssembly(childNamespace, targetNamespace, typeName);
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum HandlerType
|
||||
{
|
||||
None,
|
||||
MessageHandler,
|
||||
RouteMessageHandler
|
||||
}
|
||||
|
||||
private sealed record MessageHandlerInfo(
|
||||
HandlerType HandlerType,
|
||||
string TypeFullName,
|
||||
string TypeName,
|
||||
uint? OpCode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
|
||||
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators;
|
||||
[Generator]
|
||||
internal partial class NetworkProtocolGenerator : IIncrementalGenerator
|
||||
{
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
var networkProtocols = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsNetworkProtocolClass(node),
|
||||
transform: static (ctx, _) => GetNetworkProtocolInfo(ctx))
|
||||
.Where(static info => info != null)
|
||||
.Collect();
|
||||
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(networkProtocols);
|
||||
|
||||
context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
|
||||
{
|
||||
if (!CompilationHelper.HasFantasyDefine(source.Left))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.INetworkProtocolRegistrar") == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateCode(spc, source.Left, source.Right!);
|
||||
});
|
||||
}
|
||||
|
||||
#region GenerateCode
|
||||
|
||||
private static void GenerateCode(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation,
|
||||
IEnumerable<NetworkProtocolTypeInfo> networkProtocolTypeInfos)
|
||||
{
|
||||
var networkProtocolTypeInfoList = networkProtocolTypeInfos.ToList();
|
||||
// 获取当前程序集名称(仅用于注释)
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
// 生成网络网络协议类型注册的类。
|
||||
GenerateNetworkProtocolTypesCode(context, assemblyName, networkProtocolTypeInfoList);
|
||||
// 生成OpCode辅助方法。
|
||||
GenerateNetworkProtocolOpCodeResolverCode(context, assemblyName, networkProtocolTypeInfoList);
|
||||
// 生成Request消息的ResponseType辅助方法。
|
||||
GenerateNetworkProtocolResponseTypesResolverCode(context, assemblyName, networkProtocolTypeInfoList);
|
||||
}
|
||||
|
||||
private static void GenerateNetworkProtocolTypesCode(
|
||||
SourceProductionContext context,
|
||||
string assemblyName,
|
||||
List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
|
||||
{
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Collection"
|
||||
);
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 IEventSystemRegistrar 接口)
|
||||
builder.AddXmlComment($"Auto-generated NetworkProtocol registration class for {assemblyName}");
|
||||
builder.BeginClass("NetworkProtocolRegistrar", "internal sealed", "INetworkProtocolRegistrar");
|
||||
builder.BeginMethod("public List<Type> GetNetworkProtocolTypes()");
|
||||
|
||||
if (networkProtocolTypeInfoList.Any())
|
||||
{
|
||||
builder.AppendLine($"return new List<Type>({networkProtocolTypeInfoList.Count})");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var system in networkProtocolTypeInfoList)
|
||||
{
|
||||
builder.AppendLine($"typeof({system.FullName}),");
|
||||
}
|
||||
builder.Unindent();
|
||||
builder.AppendLine("};");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return new List<Type>();");
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("NetworkProtocolRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static void GenerateNetworkProtocolOpCodeResolverCode(
|
||||
SourceProductionContext context,
|
||||
string assemblyName,
|
||||
List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
|
||||
{
|
||||
var routeTypeInfos = networkProtocolTypeInfoList.Where(d => d.RouteType.HasValue).ToList();
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Collection",
|
||||
"System.Runtime.CompilerServices"
|
||||
);
|
||||
builder.AppendLine();
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 INetworkProtocolOpCodeResolver 接口)
|
||||
builder.AddXmlComment($"Auto-generated NetworkProtocolOpCodeResolverRegistrar class for {assemblyName}");
|
||||
builder.BeginClass("NetworkProtocolOpCodeResolverRegistrar", "internal sealed", "INetworkProtocolOpCodeResolver");
|
||||
builder.AddXmlComment($"GetOpCodeCount");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int GetOpCodeCount()");
|
||||
builder.AppendLine($"return {networkProtocolTypeInfoList.Count};");
|
||||
builder.EndMethod();
|
||||
builder.AddXmlComment($"GetCustomRouteTypeCount");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int GetCustomRouteTypeCount()");
|
||||
builder.AppendLine($"return {routeTypeInfos.Count};");
|
||||
builder.EndMethod();
|
||||
// 开始定义GetOpCodeType方法
|
||||
builder.AddXmlComment($"GetOpCodeType");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public Type GetOpCodeType(uint opCode)");
|
||||
|
||||
if (networkProtocolTypeInfoList.Any())
|
||||
{
|
||||
builder.AppendLine("switch (opCode)");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var networkProtocolTypeInfo in networkProtocolTypeInfoList)
|
||||
{
|
||||
builder.AppendLine($"case {networkProtocolTypeInfo.OpCode}:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return typeof({networkProtocolTypeInfo.FullName});");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
builder.AppendLine("default:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return null!;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return null!;");
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
|
||||
// 开始定义GetRouteType方法
|
||||
builder.AddXmlComment($"CustomRouteType");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int? GetCustomRouteType(uint opCode)");
|
||||
|
||||
if (routeTypeInfos.Any())
|
||||
{
|
||||
builder.AppendLine("switch (opCode)");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var routeTypeInfo in routeTypeInfos)
|
||||
{
|
||||
builder.AppendLine($"case {routeTypeInfo.OpCode}:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return {routeTypeInfo.RouteType};");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
builder.AppendLine("default:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return null;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return null;");
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("NetworkProtocolOpCodeResolverRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static void GenerateNetworkProtocolResponseTypesResolverCode(
|
||||
SourceProductionContext context,
|
||||
string assemblyName,
|
||||
List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
|
||||
{
|
||||
var requestList = networkProtocolTypeInfoList.Where(d => d.ResponseType != null).ToList();
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加文件头
|
||||
builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.DataStructure.Collection",
|
||||
"System.Runtime.CompilerServices"
|
||||
);
|
||||
builder.AppendLine();
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 IEventSystemRegistrar 接口)
|
||||
builder.AddXmlComment($"Auto-generated NetworkProtocolResponseTypeResolverRegistrar class for {assemblyName}");
|
||||
builder.BeginClass("NetworkProtocolResponseTypeResolverRegistrar", "internal sealed", "INetworkProtocolResponseTypeResolver");
|
||||
builder.AddXmlComment($"GetOpCodeCount");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public int GetRequestCount()");
|
||||
builder.AppendLine($"return {requestList.Count};");
|
||||
builder.EndMethod();
|
||||
// 开始定义GetOpCodeType方法
|
||||
builder.AddXmlComment($"GetOpCodeType");
|
||||
builder.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]");
|
||||
builder.BeginMethod("public Type GetResponseType(uint opCode)");
|
||||
|
||||
if (requestList.Any())
|
||||
{
|
||||
builder.AppendLine("switch (opCode)");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
foreach (var request in requestList)
|
||||
{
|
||||
builder.AppendLine($"case {request.OpCode}:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return typeof({request.ResponseType});");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
}
|
||||
builder.AppendLine("default:");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent();
|
||||
builder.AppendLine($"return null!;");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
builder.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"return null!;");
|
||||
}
|
||||
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
// 结束类和命名空间
|
||||
builder.EndClass();
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("NetworkProtocolResponseTypeResolverRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private static NetworkProtocolTypeInfo? GetNetworkProtocolInfo(GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
var symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;
|
||||
|
||||
if (symbol == null || !symbol.IsInstantiable())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var baseType = symbol.BaseType;
|
||||
|
||||
if (baseType == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (baseType.ToDisplayString() != "Fantasy.Network.Interface.AMessage")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取 OpCode 方法的值
|
||||
uint? opCodeValue = null;
|
||||
var opCodeMethod = symbol.GetMembers("OpCode").OfType<IMethodSymbol>().FirstOrDefault();
|
||||
var opCodeSyntax = opCodeMethod?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MethodDeclarationSyntax;
|
||||
|
||||
if (opCodeSyntax?.Body != null)
|
||||
{
|
||||
var returnStatement = opCodeSyntax.Body.DescendantNodes()
|
||||
.OfType<ReturnStatementSyntax>()
|
||||
.FirstOrDefault();
|
||||
|
||||
if (returnStatement?.Expression != null)
|
||||
{
|
||||
var constantValue = context.SemanticModel.GetConstantValue(returnStatement.Expression);
|
||||
if (constantValue.HasValue && constantValue.Value is uint uintValue)
|
||||
{
|
||||
opCodeValue = uintValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!opCodeValue.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取 ResponseType 属性及其类型
|
||||
string? responseTypeName = null;
|
||||
var responseTypeProperty = symbol.GetMembers("ResponseType").OfType<IPropertySymbol>().FirstOrDefault();
|
||||
if (responseTypeProperty != null)
|
||||
{
|
||||
// 获取 ResponseType 属性的类型(例如 G2C_TestResponse)
|
||||
responseTypeName = responseTypeProperty.Type.GetFullName();
|
||||
}
|
||||
|
||||
// 获取 RouteType 属性的值
|
||||
int? routeTypeValue = null;
|
||||
var routeTypeProperty = symbol.GetMembers("RouteType").OfType<IPropertySymbol>().FirstOrDefault();
|
||||
var routeTypeSyntax = routeTypeProperty?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as PropertyDeclarationSyntax;
|
||||
if (routeTypeSyntax?.ExpressionBody != null)
|
||||
{
|
||||
var constantValue = context.SemanticModel.GetConstantValue(routeTypeSyntax.ExpressionBody.Expression);
|
||||
if (constantValue.HasValue && constantValue.Value is int intValue)
|
||||
{
|
||||
routeTypeValue = intValue;
|
||||
}
|
||||
}
|
||||
|
||||
return new NetworkProtocolTypeInfo(
|
||||
symbol.GetFullName(),
|
||||
opCodeValue.Value,
|
||||
responseTypeName,
|
||||
routeTypeValue
|
||||
);
|
||||
}
|
||||
|
||||
private static bool IsNetworkProtocolClass(SyntaxNode node)
|
||||
{
|
||||
if (node is not ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (classDecl.BaseList == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var baseTypeSyntax in classDecl.BaseList.Types)
|
||||
{
|
||||
if (baseTypeSyntax.Type.ToString().Contains("AMessage"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sealed record NetworkProtocolTypeInfo(
|
||||
string FullName,
|
||||
uint OpCode,
|
||||
string? ResponseType,
|
||||
int? RouteType);
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
using Fantasy.SourceGenerator.Common;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
// ReSharper disable MemberHidesStaticFromOuterClass
|
||||
|
||||
namespace Fantasy.SourceGenerator.Generators
|
||||
{
|
||||
/// <summary>
|
||||
/// SeparateTable 接口生成器
|
||||
/// 自动生成 SeparateTable 所需的注册代码,替代运行时反射
|
||||
/// </summary>
|
||||
[Generator]
|
||||
public partial class SeparateTableGenerator : IIncrementalGenerator
|
||||
{
|
||||
private static readonly SourceCodeBuilder SeparateTableInfo = new SourceCodeBuilder();
|
||||
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
// 查找所有标记了 SeparateTableAttribute 的类
|
||||
var attributedClasses = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsSeparateTableClass(node),
|
||||
transform: static (ctx, _) => GetSeparateTableTypeInfo(ctx))
|
||||
.Where(static info => info != null)
|
||||
.Collect();
|
||||
// 组合编译信息和找到的类型
|
||||
var compilationAndTypes = context.CompilationProvider.Combine(attributedClasses);
|
||||
// 注册源代码输出
|
||||
context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
|
||||
{
|
||||
// 检查1: 是否定义了 FANTASY_NET 预编译符号
|
||||
if (!CompilationHelper.HasFantasyNETDefine(source.Left))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查2: 是否引用了 Fantasy 框架的核心类型
|
||||
if (source.Left.GetTypeByMetadataName("Fantasy.Entitas.Interface.ISupportedSeparateTable") == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GenerateRegistrationCode(spc, source.Left, source.Right!);
|
||||
});
|
||||
}
|
||||
|
||||
private static SeparateTableTypeInfo? GetSeparateTableTypeInfo(GeneratorSyntaxContext context)
|
||||
{
|
||||
var classDecl = (ClassDeclarationSyntax)context.Node;
|
||||
|
||||
if (context.SemanticModel.GetDeclaredSymbol(classDecl) is not INamedTypeSymbol symbol)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!symbol.InheritsFrom("Fantasy.Entitas.Entity"))
|
||||
{
|
||||
// 不是 Entity,不处理
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否标记了 SeparateTableAttribute
|
||||
var separateTableAttr = symbol.GetAttributes()
|
||||
.Where(attr => attr.AttributeClass?.ToDisplayString() == "Fantasy.SeparateTable.SeparateTableAttribute").ToList();
|
||||
|
||||
if (!separateTableAttr.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var separateTableInfo = new Dictionary<string, string>();
|
||||
|
||||
foreach (var attributeData in separateTableAttr)
|
||||
{
|
||||
if (attributeData.ConstructorArguments.Length < 2)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var rootTypeSymbol = attributeData.ConstructorArguments[0].Value as INamedTypeSymbol;
|
||||
var collectionName = attributeData.ConstructorArguments[1].Value?.ToString();
|
||||
|
||||
if (rootTypeSymbol == null || collectionName == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
separateTableInfo[rootTypeSymbol.GetFullName()] = collectionName;
|
||||
}
|
||||
|
||||
return separateTableInfo.Any() ? SeparateTableTypeInfo.Create(symbol, separateTableInfo) : null;
|
||||
}
|
||||
|
||||
private static void GenerateRegistrationCode(
|
||||
SourceProductionContext context,
|
||||
Compilation compilation,
|
||||
IEnumerable<SeparateTableTypeInfo> separateTableTypeInfos)
|
||||
{
|
||||
SeparateTableInfo.Clear();
|
||||
var separateTableTypeInfoList = separateTableTypeInfos.ToList();
|
||||
// 获取当前程序集名称(仅用于注释)
|
||||
var assemblyName = compilation.AssemblyName ?? "Unknown";
|
||||
// 生成代码文件
|
||||
var builder = new SourceCodeBuilder();
|
||||
// 添加 using
|
||||
builder.AddUsings(
|
||||
"System",
|
||||
"System.Collections.Generic",
|
||||
"Fantasy.Assembly",
|
||||
"Fantasy.Entitas",
|
||||
"Fantasy.Entitas.Interface",
|
||||
"Fantasy.Async"
|
||||
);
|
||||
builder.AppendLine();
|
||||
// 开始命名空间(固定使用 Fantasy.Generated)
|
||||
builder.BeginNamespace("Fantasy.Generated");
|
||||
// 开始类定义(实现 IEntitySystemRegistrar 接口)
|
||||
builder.AddXmlComment($"Auto-generated Entity System registration class for {assemblyName}");
|
||||
builder.BeginClass("SeparateTableRegistrar", "internal sealed", "ISeparateTableRegistrar");
|
||||
// 生成字段用于存储已注册(用于 UnRegister)
|
||||
GenerateFields(builder, separateTableTypeInfoList);
|
||||
// 生成注册方法
|
||||
GenerateRegisterMethod(builder, separateTableTypeInfoList);
|
||||
// 生成反注册方法
|
||||
GenerateUnRegisterMethod(builder, separateTableTypeInfoList);
|
||||
// 结束类
|
||||
builder.EndClass();
|
||||
// 生成数据库帮助类
|
||||
builder.Append(GenerateGenerateSeparateTableGeneratedExtensions().ToString());
|
||||
// 结束命名空间
|
||||
builder.EndNamespace();
|
||||
// 输出源代码
|
||||
context.AddSource("SeparateTableRegistrar.g.cs", builder.ToString());
|
||||
}
|
||||
|
||||
private static SourceCodeBuilder GenerateGenerateSeparateTableGeneratedExtensions()
|
||||
{
|
||||
var builder = new SourceCodeBuilder();
|
||||
builder.AppendLine();
|
||||
builder.Indent(1);
|
||||
builder.AddXmlComment("分表组件扩展方法。");
|
||||
builder.AppendLine("public static class SeparateTableGeneratedExtensions");
|
||||
builder.AppendLine("{");
|
||||
builder.Indent(1);
|
||||
builder.AddXmlComment("从数据库加载指定实体的所有分表数据,并自动建立父子关系。");
|
||||
builder.BeginMethod("public static FTask LoadWithSeparateTables<T>(this T entity) where T : Entity, new()");
|
||||
builder.AppendLine("return entity.Scene.SeparateTableComponent.LoadWithSeparateTables(entity);");
|
||||
builder.EndMethod();
|
||||
builder.AddXmlComment("将实体及其所有分表组件保存到数据库中。");
|
||||
builder.BeginMethod("public static FTask PersistAggregate<T>(this T entity) where T : Entity, new()");
|
||||
builder.AppendLine("return entity.Scene.SeparateTableComponent.PersistAggregate(entity);");
|
||||
builder.EndMethod();
|
||||
builder.Unindent();
|
||||
builder.AppendLine("}");
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static void GenerateFields(SourceCodeBuilder builder, List<SeparateTableTypeInfo> separateTableTypeInfoList)
|
||||
{
|
||||
SeparateTableInfo.AppendLine("private readonly List<ISeparateTableRegistrar.SeparateTableInfo> _separateTableInfos = new List<ISeparateTableRegistrar.SeparateTableInfo>()");
|
||||
SeparateTableInfo.Indent(2);
|
||||
SeparateTableInfo.AppendLine("{");
|
||||
SeparateTableInfo.Indent(1);
|
||||
|
||||
if (separateTableTypeInfoList.Any())
|
||||
{
|
||||
foreach (var separateTableTypeInfo in separateTableTypeInfoList)
|
||||
{
|
||||
foreach (var separateTableInfo in separateTableTypeInfo.SeparateTableInfo)
|
||||
{
|
||||
SeparateTableInfo.AppendLine(
|
||||
$"new ISeparateTableRegistrar.SeparateTableInfo(typeof({separateTableInfo.Key}) ,typeof({separateTableTypeInfo.TypeFullName}) ,\"{separateTableInfo.Value}\"),");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SeparateTableInfo.Unindent();
|
||||
SeparateTableInfo.AppendLine("};");
|
||||
builder.AppendLine(SeparateTableInfo.ToString());
|
||||
}
|
||||
|
||||
private static void GenerateRegisterMethod(SourceCodeBuilder builder, List<SeparateTableTypeInfo> separateTableTypeInfos)
|
||||
{
|
||||
builder.BeginMethod("public List<ISeparateTableRegistrar.SeparateTableInfo> Register()");
|
||||
builder.AppendLine("return _separateTableInfos;");
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void GenerateUnRegisterMethod(SourceCodeBuilder builder, List<SeparateTableTypeInfo> separateTableTypeInfos)
|
||||
{
|
||||
builder.BeginMethod("public List<ISeparateTableRegistrar.SeparateTableInfo> UnRegister()");
|
||||
builder.AppendLine("return _separateTableInfos;");
|
||||
builder.EndMethod();
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static bool IsSeparateTableClass(SyntaxNode node)
|
||||
{
|
||||
if (node is not ClassDeclarationSyntax classDecl)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 快速检查是否有任何 Attribute
|
||||
if (classDecl.AttributeLists.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否标记了 SeparateTableAttribute
|
||||
// 这里只做简单的语法级别检查,精确的语义检查在 GetSeparateTableTypeInfo 中进行
|
||||
foreach (var attributeList in classDecl.AttributeLists)
|
||||
{
|
||||
foreach (var attribute in attributeList.Attributes)
|
||||
{
|
||||
var attributeName = attribute.Name.ToString();
|
||||
|
||||
// 匹配以下情况:
|
||||
// 1. [SeparateTable(...)]
|
||||
// 2. [SeparateTableAttribute(...)]
|
||||
// 3. [Fantasy.Entitas.Interface.SeparateTable(...)]
|
||||
// 4. [Fantasy.Entitas.Interface.SeparateTableAttribute(...)]
|
||||
// 5. [global::Fantasy.Entitas.Interface.SeparateTable(...)]
|
||||
if (attributeName == "SeparateTable" ||
|
||||
attributeName == "SeparateTableAttribute" ||
|
||||
attributeName == "Fantasy.Entitas.Interface.SeparateTable" ||
|
||||
attributeName == "Fantasy.Entitas.Interface.SeparateTableAttribute" ||
|
||||
attributeName == "global::Fantasy.Entitas.Interface.SeparateTable" ||
|
||||
attributeName == "global::Fantasy.Entitas.Interface.SeparateTableAttribute")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private sealed class SeparateTableTypeInfo
|
||||
{
|
||||
public readonly Dictionary<string, string> SeparateTableInfo;
|
||||
public readonly string TypeFullName;
|
||||
public readonly string TypeName;
|
||||
|
||||
private SeparateTableTypeInfo(string typeFullName, string typeName,
|
||||
Dictionary<string, string> separateTableInfo)
|
||||
{
|
||||
TypeFullName = typeFullName;
|
||||
TypeName = typeName;
|
||||
SeparateTableInfo = separateTableInfo;
|
||||
}
|
||||
|
||||
public static SeparateTableTypeInfo Create(INamedTypeSymbol symbol,
|
||||
Dictionary<string, string> separateTableInfo)
|
||||
{
|
||||
return new SeparateTableTypeInfo(
|
||||
symbol.GetFullName(),
|
||||
symbol.Name,
|
||||
separateTableInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user