From 59529419e52985a53d6579c276e7707983691150 Mon Sep 17 00:00:00 2001 From: hzhang Date: Fri, 7 Feb 2025 02:30:40 +0000 Subject: [PATCH] improve: move to src --- src/AssetInfo.cs | 25 +++++++++++ src/AssetProcessGenerator.cs | 80 ++++++++++++++++++++++++++++++++++ src/AssetRegisterGenerator.cs | 52 ++++++++++++++++++++++ src/ClassSyntaxReceiver.cs | 18 ++++++++ src/GlobalRegistryGenerator.cs | 56 ++++++++++++++++++++++++ src/RegistryEntityGenerator.cs | 33 ++++++++++++++ 6 files changed, 264 insertions(+) create mode 100644 src/AssetInfo.cs create mode 100644 src/AssetProcessGenerator.cs create mode 100644 src/AssetRegisterGenerator.cs create mode 100644 src/ClassSyntaxReceiver.cs create mode 100644 src/GlobalRegistryGenerator.cs create mode 100644 src/RegistryEntityGenerator.cs diff --git a/src/AssetInfo.cs b/src/AssetInfo.cs new file mode 100644 index 0000000..e79f15e --- /dev/null +++ b/src/AssetInfo.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Polonium.Generators; + +public struct AssetInfo +{ + public INamedTypeSymbol Symbol; + public string Path; + public ClassDeclarationSyntax DeclarationSyntax; + public string ClassName; + public string ClassFullName; + public string GodotPath { + get + { + string rs = Path.Replace("\\", "/").Replace(" ", "%20"); + rs = rs.Substring(rs.IndexOf("Assets", StringComparison.Ordinal)); + return "res://" + rs; + } + } + + public string ScenePath => GodotPath.Replace(".cs", ".tscn"); + +} \ No newline at end of file diff --git a/src/AssetProcessGenerator.cs b/src/AssetProcessGenerator.cs new file mode 100644 index 0000000..7dbe304 --- /dev/null +++ b/src/AssetProcessGenerator.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using System.IO; +using Microsoft.CodeAnalysis; + +namespace Polonium.Generators; + +public abstract class AssetProcessGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + context.RegisterForSyntaxNotifications(() => new ClassSyntaxReceiver()); + } + + public abstract void Execute(GeneratorExecutionContext context); + + protected IEnumerable GetAssetsOfType(GeneratorExecutionContext context, INamedTypeSymbol? t) + { + if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver)) + yield break; + Compilation compilation = context.Compilation; + foreach (var derivedClassDeclaration in receiver.Classes) + { + SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree); + INamedTypeSymbol? derivedClassSymbol = model.GetDeclaredSymbol(derivedClassDeclaration) as INamedTypeSymbol; + string filePath = derivedClassDeclaration.SyntaxTree.FilePath; + string className = Path.GetFileNameWithoutExtension(filePath); + if(derivedClassSymbol == null || !filePath.Contains("Assets")) + continue; + AssetInfo res = new AssetInfo + { + DeclarationSyntax = derivedClassDeclaration, + ClassName = className, + ClassFullName = derivedClassSymbol.ToDisplayString(), + Path = filePath, + Symbol = derivedClassSymbol + }; + if (IsDerivedFrom(derivedClassSymbol, t)) + { + yield return res; + continue; + } + foreach (INamedTypeSymbol interfaceSymbol in derivedClassSymbol.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(interfaceSymbol, t)) + { + yield return res; + break; + } + } + + } + } + + protected IEnumerable GetAssetsOfType(GeneratorExecutionContext context, string t) + { + + Compilation compilation = context.Compilation; + INamedTypeSymbol? specificType = compilation.GetTypeByMetadataName(t); + return GetAssetsOfType(context, specificType); + } + protected static bool IsDerivedFrom(INamedTypeSymbol? symbol, INamedTypeSymbol? baseType) + { + if (symbol == null) + return false; + if (baseType == null) + return true; + INamedTypeSymbol? current = symbol.BaseType; + + while (current != null) + { + if (SymbolEqualityComparer.Default.Equals(current, baseType)) + return true; + current = current.BaseType; + } + + return false; + } + + +} diff --git a/src/AssetRegisterGenerator.cs b/src/AssetRegisterGenerator.cs new file mode 100644 index 0000000..cbad384 --- /dev/null +++ b/src/AssetRegisterGenerator.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Polonium.Generators; +[Generator] +public class AssetRegisterGenerator : AssetProcessGenerator +{ + public override void Execute(GeneratorExecutionContext context) + { + if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver)) + return; + Compilation compilation = context.Compilation; + StringBuilder sb = new(); + sb + .AppendLine("using System;") + .AppendLine("using Godot;") + .AppendLine("using Polonium.Attributes;") + .AppendLine("[AutoRegister]") + .AppendLine("public static class AssetRegister") + .AppendLine("{") + .AppendLine(" public static void Register()") + .AppendLine(" {"); + foreach (var derivedClassDeclaration in receiver.Classes) + { + SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree); + INamedTypeSymbol? symbol = model.GetDeclaredSymbol(derivedClassDeclaration) as INamedTypeSymbol; + string filePath = derivedClassDeclaration.SyntaxTree.FilePath.Replace(".cs", ".tscn"); + if(!filePath.Contains("Assets") || symbol == null) + continue; + ImmutableArray additionalFiles = context.AdditionalFiles; + if (additionalFiles.Select(y => y.Path).Contains(filePath)) + { + string rs = filePath.Replace("\\", "/").Replace(" ", "%20"); + rs = rs.Substring(rs.IndexOf("Assets", StringComparison.Ordinal)); + string gdPath = "res://" + rs; + string className = Path.GetFileNameWithoutExtension(filePath); + sb + .AppendLine( + $" GlobalRegistry.Asset<{className}>.Scene = ResourceLoader.Load(\"{gdPath}\");" + ); + } + } + sb + .AppendLine(" }") + .AppendLine("}"); + context.AddSource("AssetRegistry.g.cs", sb.ToString()); + } +} \ No newline at end of file diff --git a/src/ClassSyntaxReceiver.cs b/src/ClassSyntaxReceiver.cs new file mode 100644 index 0000000..d07047f --- /dev/null +++ b/src/ClassSyntaxReceiver.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Polonium.Generators; + +public class ClassSyntaxReceiver : ISyntaxReceiver +{ + public List Classes { get; set; } = new (); + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (syntaxNode is ClassDeclarationSyntax classDeclaration) + { + Classes.Add(classDeclaration); + } + } +} \ No newline at end of file diff --git a/src/GlobalRegistryGenerator.cs b/src/GlobalRegistryGenerator.cs new file mode 100644 index 0000000..fdea7ed --- /dev/null +++ b/src/GlobalRegistryGenerator.cs @@ -0,0 +1,56 @@ +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Polonium.Generators; +[Generator] +public class GlobalRegistryGenerator : ISourceGenerator +{ + public void Initialize(GeneratorInitializationContext context) + { + } + + public void Execute(GeneratorExecutionContext context) + { + StringBuilder sb = new(); + sb + .AppendLine("using Godot;") + .AppendLine("using System;") + .AppendLine("using System.Collections.Generic;") + .AppendLine("using System.Linq;") + .AppendLine("using System.Reflection;") + .AppendLine("using Polonium.Attributes;") + .AppendLine("using Polonium;") + .AppendLine("public static partial class GlobalRegistry") + .AppendLine("{") + .AppendLine(" public static void Start()") + .AppendLine(" {") + .AppendLine(" PoloniumRegistry.Prepare();") + .AppendLine(" Assembly assembly = Assembly.GetExecutingAssembly();") + .AppendLine(" IEnumerable registerTypes = Utils.GetLoadableTypes(assembly);") + .AppendLine(" registerTypes = registerTypes.Where(t => t.IsClass && t.GetCustomAttributes(typeof(AutoRegister), false).Any());") + .AppendLine(" foreach(Type t in registerTypes)") + .AppendLine(" {") + .AppendLine(" MethodInfo registerMethod = t.GetMethod(\"Register\", BindingFlags.Static | BindingFlags.Public);") + .AppendLine(" if (registerMethod != null)") + .AppendLine(" registerMethod.Invoke(null, null);") + .AppendLine(" }") + .AppendLine(" }") + .AppendLine(" public static class Asset where T : Node") + .AppendLine(" {") + .AppendLine(" private static readonly Queue Pool = new();") + .AppendLine(" public static PackedScene Scene { get; set; }") + .AppendLine(" public static T Instance => Scene.Instantiate();") + .AppendLine(" public static T Get() => Pool.Count > 0 ? Pool.Dequeue() : Instance;") + .AppendLine(" public static void Return(T obj)") + .AppendLine(" {") + .AppendLine(" if(Pool.Count < 10)") + .AppendLine(" Pool.Enqueue(obj);") + .AppendLine(" else") + .AppendLine(" obj.QueueFree();") + .AppendLine(" }") + .AppendLine(" }") + .AppendLine(" public static PoloniumRegistry PoloniumRegistry => PoloniumRegistry.Instance;") + .AppendLine("}"); + context.AddSource("GlobalRegistry.g.cs", sb.ToString()); + } +} \ No newline at end of file diff --git a/src/RegistryEntityGenerator.cs b/src/RegistryEntityGenerator.cs new file mode 100644 index 0000000..9de7a7c --- /dev/null +++ b/src/RegistryEntityGenerator.cs @@ -0,0 +1,33 @@ +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Polonium.Generators; +[Generator] +public class RegistryEntityGenerator : AssetProcessGenerator +{ + public override void Execute(GeneratorExecutionContext context) + { + if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver)) + return; + Compilation compilation = context.Compilation; + INamedTypeSymbol? regEntity = compilation.GetTypeByMetadataName("Polonium.Attributes.RegistryEntity"); + foreach (var derivedClassDeclaration in receiver.Classes) + { + SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree); + if ( + model.GetDeclaredSymbol(derivedClassDeclaration) is INamedTypeSymbol symbol && + symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, regEntity)) + ) + { + StringBuilder sb = new(); + sb + .AppendLine("public static partial class GlobalRegistry") + .AppendLine("{") + .AppendLine($" public static {symbol.ToDisplayString()} {symbol.Name} {{ get; set; }} ") + .AppendLine("}"); + context.AddSource($"RegistryEntity_{symbol.Name}.g.cs", sb.ToString()); + } + } + } +} \ No newline at end of file