1
0
forked from liza/Questionable

Partition quest loading into different methods

This commit is contained in:
Liza 2024-07-25 04:15:44 +02:00
parent b5589b0054
commit 042a8b5ecc
Signed by: liza
GPG Key ID: 7199F8D727D55F67
11 changed files with 189 additions and 182 deletions

View File

@ -7,13 +7,14 @@
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="xunit" Version="2.5.3"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3"/>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
</ItemGroup>
<ItemGroup>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
@ -13,6 +13,7 @@
<PackageId>QuestPathGenerator</PackageId>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
@ -23,8 +24,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
<PackageReference Include="System.Text.Json" Version="8.0.4" PrivateAssets="all" />
</ItemGroup>

View File

@ -92,7 +92,43 @@ public class QuestSourceGenerator : ISourceGenerator
if (quests.Count == 0)
return;
quests = quests.OrderBy(x => x.Item1).ToList();
var partitionedQuests = quests
.OrderBy(x => x.Item1)
.GroupBy(x => $"LoadQuests{x.Item1 / 50}")
.ToList();
List<MethodDeclarationSyntax> methods =
[
MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
Identifier("LoadQuests"))
.WithModifiers(
TokenList(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.StaticKeyword)))
.WithBody(
Block(
partitionedQuests
.Select(x =>
ExpressionStatement(
InvocationExpression(
IdentifierName(x.Key))))))
];
foreach (var partition in partitionedQuests)
{
methods.Add(MethodDeclaration(
PredefinedType(
Token(SyntaxKind.VoidKeyword)),
Identifier(partition.Key))
.WithModifiers(
TokenList(
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.StaticKeyword)))
.WithBody(
Block(CreateInitializer(partition.ToList()))));
}
var code =
CompilationUnit()
@ -132,96 +168,45 @@ public class QuestSourceGenerator : ISourceGenerator
SingletonList<MemberDeclarationSyntax>(
ClassDeclaration("AssemblyQuestLoader")
.WithModifiers(
TokenList(
[
Token(SyntaxKind.PartialKeyword)
]))
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
FieldDeclaration(
VariableDeclaration(
GenericName(
Identifier("IReadOnlyDictionary"))
.WithTypeArgumentList(
TypeArgumentList(
SeparatedList<TypeSyntax>(
new SyntaxNodeOrToken[]
{
PredefinedType(
Token(SyntaxKind
.UShortKeyword)),
Token(SyntaxKind.CommaToken),
IdentifierName("QuestRoot")
}))))
.WithVariables(
SingletonSeparatedList(
VariableDeclarator(
Identifier("Quests"))
.WithInitializer(
EqualsValueClause(
ObjectCreationExpression(
GenericName(
Identifier(
"Dictionary"))
.WithTypeArgumentList(
TypeArgumentList(
SeparatedList<
TypeSyntax>(
new
SyntaxNodeOrToken
[]
{
PredefinedType(
Token(
SyntaxKind
.UShortKeyword)),
Token(
SyntaxKind
.CommaToken),
IdentifierName(
"QuestRoot")
}))))
.WithArgumentList(
ArgumentList())
.WithInitializer(
InitializerExpression(
SyntaxKind
.CollectionInitializerExpression,
SeparatedList<
ExpressionSyntax>(
quests.SelectMany(x =>
CreateQuestInitializer(
x.Item1,
x.Item2)
.ToArray())))))))))
.WithModifiers(
TokenList(
[
Token(SyntaxKind.InternalKeyword),
Token(SyntaxKind.StaticKeyword)
]))))))))
TokenList(Token(SyntaxKind.PartialKeyword)))
.WithMembers(List<MemberDeclarationSyntax>(methods))))))
.NormalizeWhitespace();
// Add the source code to the compilation.
context.AddSource("AssemblyQuestLoader.g.cs", code.ToFullString());
}
private static IEnumerable<SyntaxNodeOrToken> CreateQuestInitializer(ushort questId, QuestRoot quest)
private static StatementSyntax[] CreateInitializer(List<(ushort QuestId, QuestRoot Root)> quests)
{
List<StatementSyntax> statements = [];
foreach (var quest in quests)
{
statements.Add(
ExpressionStatement(
InvocationExpression(
IdentifierName("AddQuest"))
.WithArgumentList(
ArgumentList(
SeparatedList<ArgumentSyntax>(
new SyntaxNodeOrToken[]
{
Argument(
LiteralExpression(SyntaxKind.NumericLiteralExpression,
Literal(quest.QuestId))),
Token(SyntaxKind.CommaToken),
Argument(CreateQuestRootExpression(quest.QuestId, quest.Root))
})))));
}
return statements.ToArray();
}
private static ObjectCreationExpressionSyntax CreateQuestRootExpression(ushort questId, QuestRoot quest)
{
try
{
return new SyntaxNodeOrToken[]
{
InitializerExpression(
SyntaxKind.ComplexElementInitializerExpression,
SeparatedList<ExpressionSyntax>(
new SyntaxNodeOrToken[]
{
LiteralExpression(
SyntaxKind.NumericLiteralExpression,
Literal(questId)),
Token(SyntaxKind.CommaToken),
ObjectCreationExpression(
return ObjectCreationExpression(
IdentifierName(nameof(QuestRoot)))
.WithInitializer(
InitializerExpression(
@ -237,11 +222,7 @@ public class QuestSourceGenerator : ISourceGenerator
AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
IdentifierName(nameof(QuestRoot.QuestSequence)),
CreateQuestSequence(quest.QuestSequence))
))))
})),
Token(SyntaxKind.CommaToken)
};
CreateQuestSequence(quest.QuestSequence))))));
}
catch (Exception e)
{

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
@ -9,13 +10,23 @@ namespace Questionable.QuestPaths;
[SuppressMessage("ReSharper", "PartialTypeWithSinglePart", Justification = "Required for RELEASE")]
public static partial class AssemblyQuestLoader
{
public static IReadOnlyDictionary<ushort, QuestRoot> GetQuests() =>
private static Dictionary<ushort, QuestRoot>? _quests;
public static IReadOnlyDictionary<ushort, QuestRoot> GetQuests()
{
if (_quests == null)
{
_quests = [];
#if RELEASE
Quests;
#else
new Dictionary<ushort, QuestRoot>();
LoadQuests();
#endif
}
return _quests ?? throw new InvalidOperationException("quest data is not initialized");
}
public static Stream QuestSchema =>
typeof(AssemblyQuestLoader).Assembly.GetManifestResourceStream("Questionable.QuestPaths.QuestSchema")!;
private static void AddQuest(ushort questId, QuestRoot root) => _quests![questId] = root;
}

View File

@ -9,35 +9,34 @@
<DebugType>none</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj"/>
<ProjectReference Include="..\QuestPathGenerator\QuestPathGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
<ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" />
<ProjectReference Include="..\QuestPathGenerator\QuestPathGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
<ItemGroup>
<None Remove="quest-v1.json"/>
<None Remove="quest-v1.json" />
<EmbeddedResource Include="quest-v1.json">
<LogicalName>Questionable.QuestPaths.QuestSchema</LogicalName>
</EmbeddedResource>
<AdditionalFiles Include="quest-v1.json"/>
<AdditionalFiles Include="quest-v1.json" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<None Remove="2.x - A Realm Reborn"/>
<None Remove="3.x - Heavensward"/>
<None Remove="4.x - Stormblood"/>
<None Remove="5.x - Shadowbringers"/>
<None Remove="6.x - Endwalker"/>
<None Remove="7.x - Dawntrail"/>
<AdditionalFiles Include="2.x - A Realm Reborn\**\*.json"/>
<AdditionalFiles Include="3.x - Heavensward\**\*.json"/>
<AdditionalFiles Include="4.x - Stormblood\**\*.json"/>
<AdditionalFiles Include="5.x - Shadowbringers\**\*.json"/>
<None Remove="2.x - A Realm Reborn" />
<None Remove="3.x - Heavensward" />
<None Remove="4.x - Stormblood" />
<None Remove="5.x - Shadowbringers" />
<None Remove="6.x - Endwalker" />
<None Remove="7.x - Dawntrail" />
<AdditionalFiles Include="2.x - A Realm Reborn\**\*.json" />
<AdditionalFiles Include="3.x - Heavensward\**\*.json" />
<AdditionalFiles Include="4.x - Stormblood\**\*.json" />
<AdditionalFiles Include="5.x - Shadowbringers\**\*.json" />
<AdditionalFiles Include="6.x - Endwalker\**\*.json" />
<AdditionalFiles Include="7.x - Dawntrail\**\*.json"/>
<AdditionalFiles Include="7.x - Dawntrail\**\*.json" />
</ItemGroup>
</Project>

View File

@ -7,6 +7,7 @@
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<DebugType>portable</DebugType>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>

View File

@ -1,46 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable", "Questionable\Questionable.csproj", "{C91EEF13-A1AC-4A40-B695-DD4E378E5989}"
# Visual Studio Version 17
VisualStudioVersion = 17.10.35013.160
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Questionable", "Questionable\Questionable.csproj", "{C91EEF13-A1AC-4A40-B695-DD4E378E5989}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLib", "LLib\LLib.csproj", "{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLib", "LLib\LLib.csproj", "{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPaths", "QuestPaths\QuestPaths.csproj", "{7A136F28-8D5C-478D-B993-0F39F1451A47}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuestPaths", "QuestPaths\QuestPaths.csproj", "{7A136F28-8D5C-478D-B993-0F39F1451A47}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPaths", "QuestPathGenerator\QuestPathGenerator.csproj", "{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuestPathGenerator", "QuestPathGenerator\QuestPathGenerator.csproj", "{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Questionable.Model", "Questionable.Model\Questionable.Model.csproj", "{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Questionable.Model", "Questionable.Model\Questionable.Model.csproj", "{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuestPathGenerator.Tests", "QuestPathGenerator.Tests\QuestPathGenerator.Tests.csproj", "{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuestPathGenerator.Tests", "QuestPathGenerator.Tests\QuestPathGenerator.Tests.csproj", "{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.Build.0 = Release|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.Build.0 = Release|Any CPU
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.Build.0 = Release|Any CPU
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.Build.0 = Release|Any CPU
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.Build.0 = Release|Any CPU
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|Any CPU.Build.0 = Release|Any CPU
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|x64.ActiveCfg = ExportRelease|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|x64.Build.0 = ExportRelease|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|x64.ActiveCfg = ExportRelease|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|x64.Build.0 = ExportRelease|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|x64.ActiveCfg = Debug|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|x64.Build.0 = Debug|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|x64.ActiveCfg = Release|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|x64.Build.0 = Release|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|x64.ActiveCfg = Debug|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|x64.Build.0 = Debug|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|x64.ActiveCfg = Release|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|x64.Build.0 = Release|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|x64.ActiveCfg = Debug|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|x64.Build.0 = Debug|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|x64.ActiveCfg = Release|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|x64.Build.0 = Release|x64
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|x64.ActiveCfg = Debug|x64
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|x64.Build.0 = Debug|x64
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|x64.ActiveCfg = Release|x64
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -423,7 +423,6 @@ internal sealed class QuestController
_logger.LogError(e, "Failed to update task {TaskName}", _currentTask.ToString());
_chatGui.PrintError(
$"[Questionable] Failed to update task '{_currentTask}', please check /xllog for details.");
Stop("Task failed to start");
Stop("Task failed to update");
return;
}

View File

@ -63,7 +63,7 @@ internal sealed class QuestRegistry
}
ValidateQuests();
_logger.LogInformation("Loaded {Count} quests", _quests.Count);
_logger.LogInformation("Loaded {Count} quests in total", _quests.Count);
}
[Conditional("RELEASE")]
@ -82,6 +82,8 @@ internal sealed class QuestRegistry
};
_quests[questId] = quest;
}
_logger.LogInformation("Loaded {Count} quests from assembly", _quests.Count);
}
[Conditional("DEBUG")]

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -37,6 +38,8 @@ internal sealed class QuestValidator
public void Validate(IEnumerable<Quest> quests)
{
Task.Factory.StartNew(() =>
{
try
{
foreach (var quest in quests)
{
@ -44,7 +47,9 @@ internal sealed class QuestValidator
{
foreach (var issue in validator.Validate(quest))
{
var level = issue.Severity == EIssueSeverity.Error ? LogLevel.Warning : LogLevel.Information;
var level = issue.Severity == EIssueSeverity.Error
? LogLevel.Warning
: LogLevel.Information;
_logger.Log(level,
"Validation failed: {QuestId} ({QuestName}) / {QuestSequence} / {QuestStep} - {Description}",
issue.QuestId, quest.Info.Name, issue.Sequence, issue.Step, issue.Description);
@ -58,6 +63,11 @@ internal sealed class QuestValidator
.ThenBy(x => x.Step)
.ThenBy(x => x.Description)
.ToList();
}
catch (Exception e)
{
_logger.LogError(e, "Unable to validate quests");
}
}, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}

View File

@ -228,10 +228,10 @@ internal sealed class QuestSelectionWindow : LWindow
ImGui.SameLine();
if (knownQuest != null &&
knownQuest.FindSequence(0)?.LastStep()?.InteractionType == EInteractionType.AcceptQuest /* &&
knownQuest.FindSequence(0)?.LastStep()?.InteractionType == EInteractionType.AcceptQuest &&
!_gameFunctions.IsQuestAccepted(quest.QuestId) &&
!_gameFunctions.IsQuestLocked(quest.QuestId) &&
(quest.IsRepeatable || !_gameFunctions.IsQuestAcceptedOrComplete(quest.QuestId))*/)
(quest.IsRepeatable || !_gameFunctions.IsQuestAcceptedOrComplete(quest.QuestId)))
{
ImGui.BeginDisabled(_questController.NextQuest != null || _questController.SimulatedQuest != null);