</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="Json.More.Net" Version="2.0.1.2" GeneratePathProperty="true" />
+ <PackageReference Include="JsonPointer.Net" Version="5.0.0" GeneratePathProperty="true" />
+ <PackageReference Include="JsonSchema.Net" Version="7.0.4" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" />
</ItemGroup>
+ <PropertyGroup>
+ <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
+ </PropertyGroup>
+
<Target Name="GetDependencyTargetPaths" AfterTargets="ResolvePackageDependenciesForBuild">
<ItemGroup>
<TargetPathWithTargetPlatformMoniker Include="..\Questionable.Model\$(OutputPath)\*.dll" IncludeRuntimeDependency="false" />
+ <TargetPathWithTargetPlatformMoniker Include="$(PkgJson_More_Net)\lib\netstandard2.0\Json.More.dll" IncludeRuntimeDependency="false" />
+ <TargetPathWithTargetPlatformMoniker Include="$(PkgJsonPointer_Net)\lib\netstandard2.0\JsonPointer.Net.dll" IncludeRuntimeDependency="false" />
+ <TargetPathWithTargetPlatformMoniker Include="$(PkgJsonSchema_Net)\lib\netstandard2.0\JsonSchema.Net.dll" IncludeRuntimeDependency="false" />
</ItemGroup>
</Target>
</Project>
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json;
+using System.Text.Json.Nodes;
+using Json.Schema;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
/// When using a simple text file as a baseline, we can create a non-incremental source generator.
/// </summary>
[Generator]
+[SuppressMessage("MicrosoftCodeAnalysisReleaseTracking", "RS2008")]
public class QuestSourceGenerator : ISourceGenerator
{
+ private static readonly DiagnosticDescriptor InvalidJson = new("QSG0001",
+ "Invalid JSON",
+ "Invalid quest file {0}",
+ nameof(QuestSourceGenerator),
+ DiagnosticSeverity.Error,
+ true);
+
public void Initialize(GeneratorInitializationContext context)
{
// No initialization required for this generator.
{
List<(ushort, QuestData)> quests = [];
+ // Find schema definition
+ AdditionalText jsonSchemaFile =
+ context.AdditionalFiles.Single(x => Path.GetFileName(x.Path) == "quest-v1.json");
+ var questSchema = JsonSchema.FromText(jsonSchemaFile.GetText()!.ToString());
+
// Go through all files marked as an Additional File in file properties.
foreach (var additionalFile in context.AdditionalFiles)
{
- if (additionalFile == null)
+ if (additionalFile == null || additionalFile == jsonSchemaFile)
continue;
if (Path.GetExtension(additionalFile.Path) != ".json")
if (text == null)
continue;
- var quest = JsonSerializer.Deserialize<QuestData>(text.ToString())!;
+ var questNode = JsonNode.Parse(text.ToString());
+ var evaluationResult = questSchema.Evaluate(questNode, new EvaluationOptions()
+ {
+ Culture = CultureInfo.InvariantCulture,
+ OutputFormat = OutputFormat.List
+ });
+ if (!evaluationResult.IsValid)
+ {
+ var error = Diagnostic.Create(InvalidJson,
+ null,
+ Path.GetFileName(additionalFile.Path));
+ context.ReportDiagnostic(error);
+ }
+
+ var quest = questNode.Deserialize<QuestData>()!;
quests.Add((id, quest));
}
"version": 1,
"dependencies": {
".NETStandard,Version=v2.0": {
+ "Json.More.Net": {
+ "type": "Direct",
+ "requested": "[2.0.1.2, )",
+ "resolved": "2.0.1.2",
+ "contentHash": "uF3QeiaXEfH92emz0/BWUiNtMSfxIIvgynuB0Bf1vF4s8eWTcZitBx9l+g/FDaJk5XxqBv9buQXizXKQcXFG1w==",
+ "dependencies": {
+ "System.Text.Json": "8.0.0"
+ }
+ },
+ "JsonPointer.Net": {
+ "type": "Direct",
+ "requested": "[5.0.0, )",
+ "resolved": "5.0.0",
+ "contentHash": "fm4T5w20AY6C+p5/pJr0vrXRNGgtSfHl34I1LxC9zdPwS9S3j0GiR1Mz/CVPWKDXXGDpCt1APHpCq7kn5adCfA==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Json.More.Net": "2.0.1.2"
+ }
+ },
+ "JsonSchema.Net": {
+ "type": "Direct",
+ "requested": "[7.0.4, )",
+ "resolved": "7.0.4",
+ "contentHash": "R0Hk2Tr/np4Q1NO8CBjyQsoiD1iFJyEQP20Sw7JnZCNGJoaSBe+g4b+nZqnBXPQhiqY5LGZ8JZwZkRh/eKZhEQ==",
+ "dependencies": {
+ "JsonPointer.Net": "5.0.0"
+ }
+ },
"Microsoft.CodeAnalysis.Analyzers": {
"type": "Direct",
"requested": "[3.3.4, )",
<None Remove="ARealmReborn"/>
<None Remove="Shadowbringers"/>
<None Remove="Endwalker"/>
+ <None Remove="quest-v1.json" />
<AdditionalFiles Include="ARealmReborn\**\*.json" />
<AdditionalFiles Include="Shadowbringers\**\*.json" />
<AdditionalFiles Include="Endwalker\**\*.json" />
+ <AdditionalFiles Include="quest-v1.json" />
</ItemGroup>
</Project>
"if": {
"properties": {
"InteractionType": {
- "const": "Interact"
+ "anyOf": [
+ {
+ "const": "Interact"
+ },
+ {
+ "const": "SinglePlayerDuty"
+ }
+ ]
}
}
},