From ed9ae2113deb68df71689f829d9dfa5da83c43a3 Mon Sep 17 00:00:00 2001 From: hzhang Date: Fri, 28 Feb 2025 08:49:32 +0000 Subject: [PATCH] add: Item Manager --- src/AssetProcessGenerator.cs | 70 +++++++++++++++++++ src/ClassInfo.cs | 15 ++++ .../AbstractSkinItemTemplateGenerator.cs | 42 +++++++++++ src/Generators/ActionNameRegisterGenerator.cs | 32 +++++++++ src/Generators/AssetRegisterGenerator.cs | 3 +- 5 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/ClassInfo.cs create mode 100644 src/Generators/AbstractSkinItemTemplateGenerator.cs create mode 100644 src/Generators/ActionNameRegisterGenerator.cs diff --git a/src/AssetProcessGenerator.cs b/src/AssetProcessGenerator.cs index 16afd2e..34ff108 100644 --- a/src/AssetProcessGenerator.cs +++ b/src/AssetProcessGenerator.cs @@ -86,5 +86,75 @@ public abstract class AssetProcessGenerator : ISourceGenerator } + protected IEnumerable GetClassesWithAttribute(GeneratorExecutionContext context, + INamedTypeSymbol? attribute) + { + if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver) + yield break; + Compilation compilation = context.Compilation; + foreach (ClassDeclarationSyntax? declaration in receiver.Classes) + { + SemanticModel model = compilation.GetSemanticModel(declaration.SyntaxTree); + INamedTypeSymbol? sClassSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol; + string filePath = declaration.SyntaxTree.FilePath; + string className = Path.GetFileNameWithoutExtension(filePath); + if (sClassSymbol is null) + continue; + if (HasAttribute(sClassSymbol, attribute)) + yield return new ClassInfo + { + DeclarationSyntax = declaration, + ClassName = className, + ClassFullName = sClassSymbol.ToDisplayString(), + Path = filePath, + Symbol = sClassSymbol, + Namespace = sClassSymbol.ContainingNamespace.ToDisplayString() + }; + } + } + protected static bool HasAttribute(INamedTypeSymbol? type, INamedTypeSymbol? attr) + { + if (type is null) + return false; + return type.GetAttributes().Any(a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, attr)); + } + protected IEnumerable GetClassesOfType(GeneratorExecutionContext context, INamedTypeSymbol? type) + { + if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver) + yield break; + Compilation compilation = context.Compilation; + foreach (ClassDeclarationSyntax? declaration in receiver.Classes) + { + SemanticModel model = compilation.GetSemanticModel(declaration.SyntaxTree); + INamedTypeSymbol? sClassSymbol = model.GetDeclaredSymbol(declaration) as INamedTypeSymbol; + string filePath = declaration.SyntaxTree.FilePath; + string className = Path.GetFileNameWithoutExtension(filePath); + if (sClassSymbol is null) + continue; + ClassInfo res = new ClassInfo + { + DeclarationSyntax = declaration, + ClassName = className, + ClassFullName = sClassSymbol.ToDisplayString(), + Path = filePath, + Symbol = sClassSymbol, + Namespace = sClassSymbol.ContainingNamespace.ToDisplayString() + }; + if (IsDerivedFrom(sClassSymbol, type)) + { + yield return res; + continue; + } + + foreach (INamedTypeSymbol interfaceSymbol in sClassSymbol.AllInterfaces) + { + if (SymbolEqualityComparer.Default.Equals(interfaceSymbol, type)) + { + yield return res; + break; + } + } + } + } } diff --git a/src/ClassInfo.cs b/src/ClassInfo.cs new file mode 100644 index 0000000..6a3b3c8 --- /dev/null +++ b/src/ClassInfo.cs @@ -0,0 +1,15 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Polonium.Generators; + +public class ClassInfo +{ + public INamedTypeSymbol Symbol { get; set; } + public ClassDeclarationSyntax DeclarationSyntax { get; set; } + public string ClassName { get; set; } + public string ClassFullName { get; set; } + public string Namespace { get; set; } + public string Path { get; set; } +} + diff --git a/src/Generators/AbstractSkinItemTemplateGenerator.cs b/src/Generators/AbstractSkinItemTemplateGenerator.cs new file mode 100644 index 0000000..9668ccb --- /dev/null +++ b/src/Generators/AbstractSkinItemTemplateGenerator.cs @@ -0,0 +1,42 @@ +using Microsoft.CodeAnalysis; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis.CSharp; + +namespace Polonium.Generators.Generators; + +public class AbstractSkinItemTemplateGenerator : AssetProcessGenerator +{ + public override void Execute(GeneratorExecutionContext context) + { + if (context.SyntaxReceiver is not ClassSyntaxReceiver receiver) + return; + Compilation compilation = context.Compilation; + INamedTypeSymbol? skinItem = compilation.GetTypeByMetadataName("Polonium.ItemManagers.SkinItem"); + foreach (ClassInfo c in GetClassesOfType(context, skinItem)) + { + if (!c.DeclarationSyntax.Modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword))) + continue; + StringBuilder sb = new(); + sb + .AppendLine("using System;") + .AppendLine("using Skin=Polonium.SkinManagers.Skin;") + .AppendLine($"namespace {c.Namespace}") + .AppendLine($"public abstract partial class {c.ClassName}") + .AppendLine("{") + .AppendLine($" public new abstract partial class Generic") + .AppendLine($" where T{c.ClassName} : Generic") + .AppendLine($" where TSkin : Skin") + .AppendLine(" {") + .AppendLine($" public new abstract class Template : {c.ClassName}.Template") + .AppendLine(" {") + .AppendLine($" public override T{c.ClassName} Get => GenericGet();") + .AppendLine($" public override void Return(T{c.ClassName} item) => GenericReturn(item);") + .AppendLine(" }") + .AppendLine(" }") + .AppendLine(" }") + .AppendLine("}"); + context.AddSource($"{c.ClassName}_AbstractSkinItemTemplate.g.cs", sb.ToString()); + } + } +} \ No newline at end of file diff --git a/src/Generators/ActionNameRegisterGenerator.cs b/src/Generators/ActionNameRegisterGenerator.cs new file mode 100644 index 0000000..ae45606 --- /dev/null +++ b/src/Generators/ActionNameRegisterGenerator.cs @@ -0,0 +1,32 @@ +using System; +using System.Text; +using Microsoft.CodeAnalysis; + +namespace Polonium.Generators.Generators; +[Generator] +public class ActionNameRegisterGenerator : AssetProcessGenerator +{ + public override void Execute(GeneratorExecutionContext context) + { + if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver)) + return; + Compilation compilation = context.Compilation; + INamedTypeSymbol? actionSyntax = compilation.GetTypeByMetadataName("Polonium.Agents.AgentAction"); + StringBuilder sb = new(); + sb + .AppendLine("using System;") + .AppendLine("using Polonium;") + .AppendLine("using Polonium.Attributes;") + .AppendLine("[AutoRegister]") + .AppendLine("public static class ActionNameRegister") + .AppendLine("{") + .AppendLine(" public static void Register()") + .AppendLine(" {"); + foreach (AssetInfo a in GetAssetsOfType(context, actionSyntax)) + sb.AppendLine($" PoloniumRegistry.Action<{a.ClassName}>.Name = \"{a.ClassName}\";"); + sb + .AppendLine(" }") + .AppendLine("}"); + context.AddSource("ActionNameRegister.g.cs", sb.ToString()); + } +} diff --git a/src/Generators/AssetRegisterGenerator.cs b/src/Generators/AssetRegisterGenerator.cs index 3fbdf91..3cbc25c 100644 --- a/src/Generators/AssetRegisterGenerator.cs +++ b/src/Generators/AssetRegisterGenerator.cs @@ -18,6 +18,7 @@ public class AssetRegisterGenerator : AssetProcessGenerator sb .AppendLine("using System;") .AppendLine("using Godot;") + .AppendLine("using Polonium;") .AppendLine("using Polonium.Attributes;") .AppendLine("[AutoRegister]") .AppendLine("public static class AssetRegister") @@ -40,7 +41,7 @@ public class AssetRegisterGenerator : AssetProcessGenerator string className = Path.GetFileNameWithoutExtension(filePath); sb .AppendLine( - $" GlobalRegistry.Asset<{className}>.Scene = ResourceLoader.Load(\"{gdPath}\");" + $" PoloniumRegistry.Asset<{className}>.Scene = ResourceLoader.Load(\"{gdPath}\");" ); } }