add: Patchable items and frames

This commit is contained in:
h z
2025-03-04 11:58:14 +00:00
parent 9668c27e2d
commit f53c66a8ec
4 changed files with 176 additions and 3 deletions

View File

@@ -86,6 +86,33 @@ public abstract class AssetProcessGenerator : ISourceGenerator
}
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)
{

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

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

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