Compare commits

...

11 Commits

Author SHA1 Message Date
f93f1eaf74 add: README 2025-03-07 16:31:40 +00:00
7f7ef09231 update: upgrade to godot 4.4.0 2025-03-04 12:26:15 +00:00
f53c66a8ec add: Patchable items and frames 2025-03-04 11:58:14 +00:00
9668c27e2d add: MessageBus 2025-03-03 11:08:24 +00:00
ed9ae2113d add: Item Manager 2025-02-28 08:49:32 +00:00
03468ad89c redesign: Agents 2025-02-27 15:39:27 +00:00
302d11e2dd add: Selectable Tile Map Layer/Mouse Controlled Camera 2025-02-26 20:17:37 +00:00
8bb977c735 add: Template Define 2025-02-22 03:56:27 +00:00
c7970c2274 improve: clean up 2025-02-20 10:42:46 +00:00
fc54380ce3 improve: better version control 2025-02-18 21:22:47 +00:00
0a3a6faca5 Merge pull request 'draft_texture_button' (#1) from draft_texture_button into master
Reviewed-on: #1
2025-02-18 14:30:55 +00:00
23 changed files with 693 additions and 42 deletions

6
.gitignore vendored
View File

@@ -2,4 +2,8 @@ bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/
/_ReSharper.Caches/
.idea/
VersionInfo.props
/summerizer.py
/summerizer

View File

@@ -1,13 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/modules.xml
/.idea.Hangman.SDK.Generators.iml
/projectSettingsUpdater.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,5 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="VersionInfo.props" Condition="Exists('VersionInfo.props')" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
@@ -8,7 +8,7 @@
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<IsRoslynComponent>true</IsRoslynComponent>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.1.1-x</Version>
<Version>$(PoloniumGeneratorsVersion)</Version>
<Authors>Hangman</Authors>
<PackageId>Polonium.Generators</PackageId>
<DisableImplicitRestore>true</DisableImplicitRestore>

View File

@@ -3,7 +3,7 @@
"profiles": {
"DebugRoslynSourceGenerator": {
"commandName": "DebugRoslynComponent",
"targetProject": "../Polonium.Generators.Test/Polonium.Generators.Test.csproj"
"targetProject": "../Polonium.Workspace/Polonium.Test/Polonium.Test.csproj"
}
}
}

36
README.md Normal file
View File

@@ -0,0 +1,36 @@
# Polonium.Generators
**Project Description**
Polonium.Generators is a C# source generator suite specifically built for the Polonium ecosystem. By analyzing attributes like `[ProxyNode]`, `[AutoPatch]`, `[RegistryEntity]`, and `[RegistryPassThrough]`, it generates boilerplate code that ties together key features in Polonium, reducing repetitive tasks and ensuring consistency.
---
## Code Generation Highlights
1. **`[ProxyNode]`**
When a class is annotated with `[ProxyNode]` (and optionally `[ProxyProperty]` for properties or `[ProxyMethod]` for methods), Polonium.Generators automatically creates partial classes or methods, simplifying script bridging and property handling. This is primarily used in Poloniums embedded classes, but it can also be extended.
2. **`[AutoPatch]` & `[PatchableProperty]`**
If an interface is marked with `[AutoPatch]` and contains at most one `[PatchableProperty]` (with a getter and setter), Polonium.Generators will create a corresponding Patch class. This allows you to apply changes ("patches") to classes implementing that interface without direct references, greatly simplifying state synchronization.
3. **`[RegistryEntity]`**
Classes annotated with `[RegistryEntity]` trigger a code generation step that adds a property for that class in `GlobalRegistry`, enabling you to easily access and manage instances across your entire project.
4. **`[RegistryPassThrough]`**
Properties marked with `[RegistryPassThrough]` are automatically bridged so that getting or setting them references the central `PoloniumRegistry`. While primarily used by Poloniums own embedded classes, it can be adapted for larger multi-module scenarios.
---
## Installation
1. Ensure youre targeting .NET 6 or higher (or the same version Polonium requires).
2. Install via NuGet:
```bash
dotnet add package Polonium.Generators
```
3. Add `<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>` in your `.csproj` if you want to inspect the generated sources.
---
## License
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).

12
publish Normal file → Executable file
View File

@@ -1,2 +1,12 @@
#!/bin/bash
dotnet nuget push "$(ls -t ./bin/Debug/Polonium.Generators.*.nupkg | head -n 1)" --source hangman-lab
SCRIPT_DIR=$(dirname "$(realpath "${BASH_SOURCE[0]}")")/
LATEST_PACKAGE=$(ls -t "${SCRIPT_DIR}"bin/Debug/Polonium.Generators.*.nupkg 2>/dev/null | head -n 1)
if [[ -z "$LATEST_PACKAGE" ]]; then
echo "❌ Error: No .nupkg file found in ${SCRIPT_DIR}/bin/Debug/"
exit 1
fi
echo "🚀 Pushing NuGet package: $LATEST_PACKAGE"
dotnet nuget push "$LATEST_PACKAGE" --source hangman-lab --skip-duplicate

View File

@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators;
@@ -10,7 +12,7 @@ public abstract class AssetProcessGenerator : ISourceGenerator
{
context.RegisterForSyntaxNotifications(() => new ClassSyntaxReceiver());
}
public abstract void Execute(GeneratorExecutionContext context);
protected IEnumerable<AssetInfo> GetAssetsOfType(GeneratorExecutionContext context, INamedTypeSymbol? t)
@@ -75,6 +77,146 @@ public abstract class AssetProcessGenerator : ISourceGenerator
return false;
}
protected static string Format(string template, string data)
{
string[] dataParts = data.Split(':');
for(int i = 0; i < dataParts.Length; i++)
template = template.Replace($"/*{i}*/", dataParts[i]);
return template;
}
protected IEnumerable<InterfaceInfo> GetInterfacesWithAttribute(GeneratorExecutionContext context,
INamedTypeSymbol? attribute)
{
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
yield break;
Compilation compilation = context.Compilation;
foreach (InterfaceDeclarationSyntax ifc in receiver.Interfaces)
{
SemanticModel model = compilation.GetSemanticModel(ifc.SyntaxTree);
INamedTypeSymbol? interfaceSymbol = model.GetDeclaredSymbol(ifc) as INamedTypeSymbol;
string filePath = ifc.SyntaxTree.FilePath;
string interfaceName = Path.GetFileNameWithoutExtension(filePath);
if(interfaceSymbol is null)
continue;
if (HasAttribute(interfaceSymbol, attribute))
yield return new InterfaceInfo
{
DeclarationSyntax = ifc,
InterfaceName = interfaceName,
InterfaceFillName = interfaceSymbol.ToDisplayString(),
Path = filePath,
Symbol = interfaceSymbol,
Namespace = interfaceSymbol.ContainingNamespace.ToDisplayString()
};
}
}
protected IEnumerable<ClassInfo> 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 IEnumerable<ClassInfo> GetClassesWithInterface(GeneratorExecutionContext context, INamedTypeSymbol? ifc, bool direct = true)
{
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);
bool interfaceFound = false;
if (sClassSymbol is null)
yield break;
if (direct)
interfaceFound = sClassSymbol.Interfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, ifc));
else
interfaceFound = sClassSymbol.AllInterfaces.Any(i => SymbolEqualityComparer.Default.Equals(i, ifc));
if (interfaceFound)
{
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<ClassInfo> 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;
}
}
}
}
}

15
src/ClassInfo.cs Normal file
View File

@@ -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; }
}

View File

@@ -6,13 +6,14 @@ namespace Polonium.Generators;
public class ClassSyntaxReceiver : ISyntaxReceiver
{
public List<ClassDeclarationSyntax> Classes { get; set; } = new ();
public List<ClassDeclarationSyntax> Classes { get; } = new ();
public List<InterfaceDeclarationSyntax> Interfaces { get; } = new ();
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
{
if (syntaxNode is ClassDeclarationSyntax classDeclaration)
{
Classes.Add(classDeclaration);
}
if(syntaxNode is InterfaceDeclarationSyntax interfaceDeclaration)
Interfaces.Add(interfaceDeclaration);
}
}

34
src/ContextExtensions.cs Normal file
View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators;
public static class ContextExtensions
{
public static IEnumerable<ClassDeclarationSyntax> GetClassesWithAttribute(this GeneratorExecutionContext context, ClassSyntaxReceiver receiver, INamedTypeSymbol? t)
{
Compilation compilation = context.Compilation;
foreach (ClassDeclarationSyntax? cls in receiver.Classes)
{
SemanticModel model = compilation.GetSemanticModel(cls.SyntaxTree);
if (
model.GetDeclaredSymbol(cls) is INamedTypeSymbol symbol &&
symbol.GetAttributes().Any(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, t))
)
yield return cls;
}
}
public static IEnumerable<AttributeData> GetAttributes(this GeneratorExecutionContext context, ClassDeclarationSyntax cls, INamedTypeSymbol? t)
{
Compilation compilation = context.Compilation;
SemanticModel model = compilation.GetSemanticModel(cls.SyntaxTree);
return model
.GetDeclaredSymbol(cls)!
.GetAttributes()
.Where(x => SymbolEqualityComparer.Default.Equals(x.AttributeClass, t));
}
}

View File

@@ -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<T{c.ClassName}, TSkin>")
.AppendLine($" where T{c.ClassName} : Generic<T{c.ClassName}, TSkin>")
.AppendLine($" where TSkin : Skin")
.AppendLine(" {")
.AppendLine($" public new abstract class Template : {c.ClassName}.Template")
.AppendLine(" {")
.AppendLine($" public override T{c.ClassName} Get => GenericGet<T{c.ClassName}, TSkin>();")
.AppendLine($" public override void Return(T{c.ClassName} item) => GenericReturn<T{c.ClassName}, TSkin>(item);")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
context.AddSource($"{c.ClassName}_AbstractSkinItemTemplate.g.cs", sb.ToString());
}
}
}

View File

@@ -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());
}
}

View File

@@ -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<PackedScene>(\"{gdPath}\");"
$" PoloniumRegistry.Asset<{className}>.Scene = ResourceLoader.Load<PackedScene>(\"{gdPath}\");"
);
}
}

View File

@@ -0,0 +1,131 @@
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class FramePatchGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? autoPatch = compilation.GetTypeByMetadataName("Polonium.Attributes.AutoPatch");
INamedTypeSymbol? patchableProperty = compilation.GetTypeByMetadataName("Polonium.Attributes.PatchableProperty");
foreach (InterfaceInfo inf in GetInterfacesWithAttribute(context, autoPatch))
{
PropertyDeclarationSyntax? underlying = inf.DeclarationSyntax.Members
.OfType<PropertyDeclarationSyntax>()
.FirstOrDefault(m => m.AttributeLists
.SelectMany(a => a.Attributes)
.Any(a =>
{
SemanticModel model = compilation.GetSemanticModel(a.SyntaxTree);
INamedTypeSymbol? attributeSymbol = model.GetSymbolInfo(a).Symbol?.ContainingType;
return SymbolEqualityComparer.Default.Equals(attributeSymbol, patchableProperty);
}));
if (underlying is null)
continue;
SemanticModel model = compilation.GetSemanticModel(underlying.SyntaxTree);
ITypeSymbol? underlyingType = model.GetTypeInfo(underlying.Type).Type;
ITypeSymbol? hashSet = compilation
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableHashSet`1");
ITypeSymbol? dictionary = compilation
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableDictionary`2");
string typeString = "";
StringBuilder sb = new();
sb
.AppendLine("using System;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using System.Linq;")
.AppendLine("using Polonium.Interfaces;")
.AppendLine("using Polonium.DataStructures.PatchableItems;")
.AppendLine("using Polonium.Resources.FramePatches;")
.AppendLine("using Polonium.Resources.FramePatches.Generic;")
.AppendLine($"namespace {inf.Namespace};");
string lName = inf.InterfaceName[0] == 'I' ? inf.InterfaceName.Substring(1) : inf.InterfaceName;
if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, hashSet))
{
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
sb
.AppendLine($"public partial class {lName}Patch : HashSetFramePatch<{x1?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine($" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
.AppendLine(" {")
.AppendLine(" case UpdateMethods.None:")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Update:")
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates)")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Add(x);")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Replace:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Custom:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
.AppendLine(" return;")
.AppendLine(" default:")
.AppendLine(" return;")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
}
else if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, dictionary))
{
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
ITypeSymbol? x2 = (underlyingType as INamedTypeSymbol)?.TypeArguments[1];
sb
.AppendLine($"public partial class {lName}Patch : DictionaryFramePatch<{x1?.ToDisplayString()}, {x2?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine(" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
.AppendLine(" {")
.AppendLine(" case UpdateMethods.None:")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Update:")
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates.Keys)")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}[x] = Updates[x];")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Replace:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Custom:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
.AppendLine(" return;")
.AppendLine(" default:")
.AppendLine(" return;")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
}
else
{
sb
.AppendLine($"public partial class {lName}Patch : FramePatch<{underlyingType?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine(" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" sFrame.{underlying.Identifier.ToString()} = Updates;")
.AppendLine(" }")
.AppendLine("}");
}
context.AddSource($"{lName}Patch.g.cs", sb.ToString());
}
}
}

View File

@@ -0,0 +1,58 @@
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class PoloniumFactoryGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
return;
StringBuilder sb = new();
sb
.AppendLine("using System;")
.AppendLine("using Polonium.Attributes;")
.AppendLine("using Polonium.Interfaces;")
.AppendLine("using Polonium.Resources;")
.AppendLine("using Polonium.MessageManager;")
.AppendLine("[AutoRegister]")
.AppendLine("public class PoloniumFactoryRegister")
.AppendLine("{")
.AppendLine(" public static void Register()")
.AppendLine(" {")
.AppendLine(" GlobalRegistry.PoloniumFactory = new PoloniumFactory();")
.AppendLine(" }")
.AppendLine(" public class PoloniumFactory : IPoloniumFactory")
.AppendLine(" {")
.AppendLine(" public MessageHeader CreateMessageHeader()")
.AppendLine(" {");
Compilation compilation = context.Compilation;
INamedTypeSymbol? signalHeader = compilation.GetTypeByMetadataName("Polonium.MessageManager.MessageHeader");
bool founded = false;
foreach (ClassDeclarationSyntax cls in receiver.Classes)
{
SemanticModel semanticModel = compilation.GetSemanticModel(cls.SyntaxTree);
INamedTypeSymbol? nCls = semanticModel.GetDeclaredSymbol(cls) as INamedTypeSymbol;
if(nCls is null)
continue;
if (IsDerivedFrom(nCls, signalHeader) && !nCls.IsAbstract)
{
sb.AppendLine($" return new {nCls.ToDisplayString()}();");
founded = true;
break;
}
}
if (!founded)
sb.AppendLine($" throw new NotImplementedException();");
sb
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
context.AddSource("PoloniumFactoryRegistry.g.cs", sb.ToString());
}
}

View File

@@ -1,6 +1,7 @@
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
@@ -12,7 +13,7 @@ public class RegistryEntityGenerator : AssetProcessGenerator
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? regEntity = compilation.GetTypeByMetadataName("Polonium.Attributes.RegistryEntity");
foreach (var derivedClassDeclaration in receiver.Classes)
foreach (ClassDeclarationSyntax? derivedClassDeclaration in receiver.Classes)
{
SemanticModel model = compilation.GetSemanticModel(derivedClassDeclaration.SyntaxTree);
if (

View File

@@ -0,0 +1,31 @@
using System.Text;
using Microsoft.CodeAnalysis;
namespace Polonium.Generators.Generators;
[Generator]
public class SendMessageMethodGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? ifc = compilation.GetTypeByMetadataName("Polonium.Interfaces.IMessageClient");
foreach (ClassInfo cls in GetClassesWithInterface(context, ifc))
{
StringBuilder sb = new();
sb
.AppendLine("using Polonium.Interfaces;")
.AppendLine("using System;")
.AppendLine("using Polonium.Resources;")
.AppendLine("using Polonium.MessageManager;")
.AppendLine($"namespace {cls.Namespace};")
.AppendLine($"public partial class {cls.ClassName}")
.AppendLine("{")
.AppendLine(" public event IMessageClient.MessageSentEventHandler MessageSent;")
.AppendLine(" public void SendMessage(PoloniumMessage msg) => MessageSent?.Invoke(msg); ")
.AppendLine("}");
context.AddSource($"{cls.ClassName}_MessageClient.g.cs", sb.ToString());
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class TemplateBlockGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? templateDefine = compilation.GetTypeByMetadataName("Polonium.Attributes.TemplateDefines.TemplateBlock");
foreach (ClassDeclarationSyntax? cls in context.GetClassesWithAttribute(receiver, templateDefine))
{
if(!cls.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
continue;
string isStatic = "";
if(cls.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
isStatic = "static";
StringBuilder sb = new StringBuilder();
sb
.AppendLine($"public {isStatic} partial class {cls.Identifier.ToString()}")
.AppendLine("{");
HashSet<string> headers = new();
foreach (AttributeData attr in context.GetAttributes(cls, templateDefine))
{
string signature = attr.ConstructorArguments[0].Value?.ToString()??string.Empty;
string template = attr.ConstructorArguments[1].Value?.ToString()??string.Empty;
string dataFile = attr.ConstructorArguments[2].Value?.ToString()??string.Empty;
string depsStr = attr.ConstructorArguments[3].Value?.ToString()??string.Empty;
string[] deps = depsStr.Split(';');
foreach (string dep in deps)
headers.Add(dep);
sb
.AppendLine($" {signature}")
.AppendLine(" {");
AdditionalText? df = context.AdditionalFiles.FirstOrDefault(f => f.Path.EndsWith(dataFile));
string[] data =(df?.GetText()?.ToString() ?? "").Split('\n');
foreach (string d in data)
{
if(d.Trim().Equals(""))
continue;
sb.AppendLine($" {Format(template, d)}");
}
sb.AppendLine(" }");
}
sb.AppendLine("}");
StringBuilder hb = new StringBuilder();
foreach (string header in headers)
{
if(header.Trim().Equals(""))
continue;
hb.AppendLine($"using {header};");
}
hb.Append(sb);
context.AddSource($"{cls.Identifier.ToString()}_TemplateDefine.g.cs", hb.ToString());
}
}
}

View File

@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class TemplateInlineGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if (!(context.SyntaxReceiver is ClassSyntaxReceiver receiver))
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? templateDefine = compilation.GetTypeByMetadataName("Polonium.Attributes.TemplateDefines.TemplateInline");
foreach (ClassDeclarationSyntax? cls in context.GetClassesWithAttribute(receiver, templateDefine))
{
if(!cls.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
continue;
string isStatic = "";
if(cls.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword)))
isStatic = "static";
StringBuilder sb = new StringBuilder();
sb
.AppendLine($"public {isStatic} partial class {cls.Identifier.ToString()}")
.AppendLine("{");
HashSet<string> headers = new();
foreach (AttributeData attr in context.GetAttributes(cls, templateDefine))
{
string template = attr.ConstructorArguments[0].Value?.ToString()??string.Empty;
string dataFile = attr.ConstructorArguments[1].Value?.ToString()??string.Empty;
string depsString = attr.ConstructorArguments[2].Value?.ToString()??string.Empty;
string[] deps = depsString.Split(';');
foreach (string dep in deps)
headers.Add(dep);
AdditionalText? df = context.AdditionalFiles.FirstOrDefault(f => f.Path.EndsWith(dataFile));
string[] data =(df?.GetText()?.ToString() ?? "").Split('\n');
foreach (string d in data)
{
if(d.Trim().Equals(""))
continue;
sb.AppendLine($" {Format(template, d)}");
}
}
sb.AppendLine("}");
StringBuilder hb = new();
foreach(string header in headers)
{
if(header.Trim().Equals(""))
continue;
hb.AppendLine($"using {header};");
}
hb.Append(sb);
Console.WriteLine(hb.ToString());
context.AddSource($"{cls.Identifier.ToString()}_TemplateInline.g.cs", hb.ToString());
}
}
}

14
src/InterfaceInfo.cs Normal file
View File

@@ -0,0 +1,14 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators;
public class InterfaceInfo
{
public INamedTypeSymbol Symbol { get; set; }
public InterfaceDeclarationSyntax DeclarationSyntax { get; set; }
public string InterfaceName { get; set; }
public string InterfaceFillName { get; set; }
public string Namespace { get; set; }
public string Path { get; set; }
}