Compare commits

..

15 Commits

Author SHA1 Message Date
400cc3a048 test commit 2025-05-21 16:22:20 +01:00
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
e97b2f098f add: sdk 2025-02-18 11:41:05 +00:00
6738080639 refactor: redesign project structure 2025-02-16 22:36:38 +00:00
24a74a6f80 draft: texture button 2025-02-15 08:25:01 +00:00
25 changed files with 694 additions and 152 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,15 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="VersionInfo.props" Condition="Exists('VersionInfo.props')" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<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>
@@ -33,18 +32,13 @@
</Target>
<Target Name="CopyPackageToLocalFeed" AfterTargets="Pack">
<Message Text="Executing Copy Pack Task" Importance="high" />
<Message Text="OutputPath: $(ProjectDir)$(OutputPath)" Importance="high" />
<ItemGroup>
<NuGetPackages Include="$(OutputPath)../*.nupkg" />
</ItemGroup>
<Message Text="Printing pkgs--------------------" Importance="high"/>
<Message Text="Pkgs: @(NuGetPackages)" Importance="high"/>
<Copy SourceFiles="@(NuGetPackages)" DestinationFolder="/NuGetFeed"/>
</Target>
<Target Name="RestoreNoCache" BeforeTargets="Restore">
<Message Text="Restoring packages with no chache" Importance="high"/>
<Target Name="RestoreNoCache" BeforeTargets="CoreCompile">
<Exec Command="dotnet restore --no-cache"/>
</Target>
</Project>

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

@@ -1,59 +0,0 @@
using System.Text;
using Microsoft.CodeAnalysis;
namespace Polonium.Generators.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("using Polonium.Interfaces;")
.AppendLine("public static partial class GlobalRegistry")
.AppendLine("{")
.AppendLine(" public static void Start()")
.AppendLine(" {")
.AppendLine(" PoloniumRegistry.Prepare();")
.AppendLine(" Assembly assembly = Assembly.GetExecutingAssembly();")
.AppendLine(" IEnumerable<Type> 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<T> where T : Node")
.AppendLine(" {")
.AppendLine(" private static readonly Queue<T> Pool = new();")
.AppendLine(" public static PackedScene Scene { get; set; }")
.AppendLine(" public static T Instance => Scene.Instantiate<T>();")
.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(" public static bool Paused { get; set; }")
.AppendLine(" public static HashSet<ITimeConsumer> TimeConsumers { get; } = new ();")
.AppendLine("}");
context.AddSource("GlobalRegistry.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,44 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
namespace Polonium.Generators.Generators;
[Generator]
public class ProxyNodeGenerator : AssetProcessGenerator
{
private INamedTypeSymbol? NodeProxy { get; set; }
private IEnumerable<INamedTypeSymbol> ProxyNodesInNamespace(INamespaceSymbol ns)
{
foreach (INamespaceOrTypeSymbol member in ns.GetMembers())
{
if (member is INamespaceSymbol nsx)
foreach (INamedTypeSymbol nsz in ProxyNodesInNamespace(nsx))
yield return nsz;
else if (member is INamedTypeSymbol nsu)
if (nsu.GetAttributes().Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, NodeProxy)))
yield return nsu;
}
}
public override void Execute(GeneratorExecutionContext context)
{
Compilation compilation = context.Compilation;
NodeProxy = compilation.GetTypeByMetadataName("Polonium.Attributes.ProxyNode");
foreach (INamedTypeSymbol node in ProxyNodesInNamespace(compilation.GlobalNamespace))
{
StringBuilder sb = new();
sb
.AppendLine($"namespace Polonium.Nodes;")
.AppendLine("using Godot;")
.AppendLine($"[GlobalClass]")
.AppendLine($"[Tool]")
.AppendLine($"public partial class {node.Name} : {node.ToDisplayString()}")
.AppendLine("{")
.AppendLine("}");
context.AddSource($"NodeProxy_{node.Name}.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; }
}