168 lines
7.2 KiB
C#
168 lines
7.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using Json.Schema;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Questionable.Model.Gathering;
|
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
|
using static Questionable.QuestPathGenerator.RoslynShortcuts;
|
|
|
|
namespace Questionable.QuestPathGenerator;
|
|
|
|
[Generator]
|
|
[SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008")]
|
|
public class GatheringSourceGenerator : ISourceGenerator
|
|
{
|
|
private static readonly DiagnosticDescriptor InvalidJson = new("GPG0001",
|
|
"Invalid JSON",
|
|
"Invalid gathering file: {0}",
|
|
nameof(GatheringSourceGenerator),
|
|
DiagnosticSeverity.Error,
|
|
true);
|
|
|
|
public void Initialize(GeneratorInitializationContext context)
|
|
{
|
|
// No initialization required for this generator.
|
|
}
|
|
|
|
public void Execute(GeneratorExecutionContext context)
|
|
{
|
|
// Find schema definition
|
|
AdditionalText? gatheringSchema =
|
|
context.AdditionalFiles.SingleOrDefault(x => Path.GetFileName(x.Path) == "gatheringlocation-v1.json");
|
|
if (gatheringSchema != null)
|
|
GenerateGatheringSource(context, gatheringSchema);
|
|
}
|
|
|
|
private void GenerateGatheringSource(GeneratorExecutionContext context, AdditionalText jsonSchemaFile)
|
|
{
|
|
var gatheringSchema = JsonSchema.FromText(jsonSchemaFile.GetText()!.ToString());
|
|
|
|
List<(ushort, GatheringRoot)> gatheringLocations = [];
|
|
foreach (var (id, node) in Utils.GetAdditionalFiles(context, jsonSchemaFile, gatheringSchema, InvalidJson,
|
|
ushort.Parse))
|
|
{
|
|
var gatheringLocation = node.Deserialize<GatheringRoot>()!;
|
|
gatheringLocations.Add((id, gatheringLocation));
|
|
}
|
|
|
|
if (gatheringLocations.Count == 0)
|
|
return;
|
|
|
|
var partitionedLocations = gatheringLocations
|
|
.OrderBy(x => x.Item1)
|
|
.GroupBy(x => $"LoadLocation{x.Item1 / 100}")
|
|
.ToList();
|
|
|
|
var methods = Utils.CreateMethods("LoadLocations", partitionedLocations, CreateInitializer);
|
|
|
|
var code =
|
|
CompilationUnit()
|
|
.WithUsings(
|
|
List(
|
|
new[]
|
|
{
|
|
UsingDirective(
|
|
IdentifierName("System")),
|
|
UsingDirective(
|
|
QualifiedName(
|
|
IdentifierName("System"),
|
|
IdentifierName("Numerics"))),
|
|
UsingDirective(
|
|
QualifiedName(
|
|
IdentifierName("System"),
|
|
IdentifierName("IO"))),
|
|
UsingDirective(
|
|
QualifiedName(
|
|
QualifiedName(
|
|
IdentifierName("System"), IdentifierName("Collections")),
|
|
IdentifierName("Generic"))),
|
|
UsingDirective(
|
|
QualifiedName(
|
|
QualifiedName(
|
|
IdentifierName("Questionable"),
|
|
IdentifierName("Model")),
|
|
IdentifierName("Gathering"))),
|
|
UsingDirective(
|
|
QualifiedName(
|
|
QualifiedName(
|
|
IdentifierName("Questionable"),
|
|
IdentifierName("Model")),
|
|
IdentifierName("Common")))
|
|
}))
|
|
.WithMembers(
|
|
SingletonList<MemberDeclarationSyntax>(
|
|
FileScopedNamespaceDeclaration(
|
|
QualifiedName(
|
|
IdentifierName("Questionable"),
|
|
IdentifierName("GatheringPaths")))
|
|
.WithMembers(
|
|
SingletonList<MemberDeclarationSyntax>(
|
|
ClassDeclaration("AssemblyGatheringLocationLoader")
|
|
.WithModifiers(
|
|
TokenList(Token(SyntaxKind.PartialKeyword)))
|
|
.WithMembers(List<MemberDeclarationSyntax>(methods))))))
|
|
.NormalizeWhitespace();
|
|
|
|
// Add the source code to the compilation.
|
|
context.AddSource("AssemblyGatheringLocationLoader.g.cs", code.ToFullString());
|
|
}
|
|
|
|
private static StatementSyntax[] CreateInitializer(List<(ushort QuestId, GatheringRoot Root)> quests)
|
|
{
|
|
List<StatementSyntax> statements = [];
|
|
|
|
foreach (var quest in quests)
|
|
{
|
|
statements.Add(
|
|
ExpressionStatement(
|
|
InvocationExpression(
|
|
IdentifierName("AddLocation"))
|
|
.WithArgumentList(
|
|
ArgumentList(
|
|
SeparatedList<ArgumentSyntax>(
|
|
new SyntaxNodeOrToken[]
|
|
{
|
|
Argument(
|
|
LiteralExpression(SyntaxKind.NumericLiteralExpression,
|
|
Literal(quest.QuestId))),
|
|
Token(SyntaxKind.CommaToken),
|
|
Argument(CreateGatheringRootExpression(quest.QuestId, quest.Root))
|
|
})))));
|
|
}
|
|
|
|
return statements.ToArray();
|
|
}
|
|
|
|
private static ObjectCreationExpressionSyntax CreateGatheringRootExpression(ushort locationId, GatheringRoot root)
|
|
{
|
|
try
|
|
{
|
|
var emptyRoot = new GatheringRoot();
|
|
return ObjectCreationExpression(
|
|
IdentifierName(nameof(GatheringRoot)))
|
|
.WithInitializer(
|
|
InitializerExpression(
|
|
SyntaxKind.ObjectInitializerExpression,
|
|
SeparatedList<ExpressionSyntax>(
|
|
SyntaxNodeList(
|
|
AssignmentList(nameof(GatheringRoot.Author), root.Author).AsSyntaxNodeOrToken(),
|
|
AssignmentList(nameof(GatheringRoot.Steps), root.Steps)
|
|
.AsSyntaxNodeOrToken(),
|
|
Assignment(nameof(GatheringRoot.FlyBetweenNodes), root.FlyBetweenNodes,
|
|
emptyRoot.FlyBetweenNodes)
|
|
.AsSyntaxNodeOrToken(),
|
|
AssignmentList(nameof(GatheringRoot.Groups), root.Groups).AsSyntaxNodeOrToken()))));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
throw new Exception($"GatheringGen[{locationId}]: {e.Message}", e);
|
|
}
|
|
}
|
|
}
|