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,6 +7,7 @@
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
@ -13,6 +13,7 @@
<PackageId>QuestPathGenerator</PackageId> <PackageId>QuestPathGenerator</PackageId>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -92,7 +92,43 @@ public class QuestSourceGenerator : ISourceGenerator
if (quests.Count == 0) if (quests.Count == 0)
return; 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 = var code =
CompilationUnit() CompilationUnit()
@ -132,96 +168,45 @@ public class QuestSourceGenerator : ISourceGenerator
SingletonList<MemberDeclarationSyntax>( SingletonList<MemberDeclarationSyntax>(
ClassDeclaration("AssemblyQuestLoader") ClassDeclaration("AssemblyQuestLoader")
.WithModifiers( .WithModifiers(
TokenList( TokenList(Token(SyntaxKind.PartialKeyword)))
[ .WithMembers(List<MemberDeclarationSyntax>(methods))))))
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)
]))))))))
.NormalizeWhitespace(); .NormalizeWhitespace();
// Add the source code to the compilation. // Add the source code to the compilation.
context.AddSource("AssemblyQuestLoader.g.cs", code.ToFullString()); 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 try
{ {
return new SyntaxNodeOrToken[] return ObjectCreationExpression(
{
InitializerExpression(
SyntaxKind.ComplexElementInitializerExpression,
SeparatedList<ExpressionSyntax>(
new SyntaxNodeOrToken[]
{
LiteralExpression(
SyntaxKind.NumericLiteralExpression,
Literal(questId)),
Token(SyntaxKind.CommaToken),
ObjectCreationExpression(
IdentifierName(nameof(QuestRoot))) IdentifierName(nameof(QuestRoot)))
.WithInitializer( .WithInitializer(
InitializerExpression( InitializerExpression(
@ -237,11 +222,7 @@ public class QuestSourceGenerator : ISourceGenerator
AssignmentExpression( AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression, SyntaxKind.SimpleAssignmentExpression,
IdentifierName(nameof(QuestRoot.QuestSequence)), IdentifierName(nameof(QuestRoot.QuestSequence)),
CreateQuestSequence(quest.QuestSequence)) CreateQuestSequence(quest.QuestSequence))))));
))))
})),
Token(SyntaxKind.CommaToken)
};
} }
catch (Exception e) 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.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -9,13 +10,23 @@ namespace Questionable.QuestPaths;
[SuppressMessage("ReSharper", "PartialTypeWithSinglePart", Justification = "Required for RELEASE")] [SuppressMessage("ReSharper", "PartialTypeWithSinglePart", Justification = "Required for RELEASE")]
public static partial class AssemblyQuestLoader 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 #if RELEASE
Quests; LoadQuests();
#else
new Dictionary<ushort, QuestRoot>();
#endif #endif
}
return _quests ?? throw new InvalidOperationException("quest data is not initialized");
}
public static Stream QuestSchema => public static Stream QuestSchema =>
typeof(AssemblyQuestLoader).Assembly.GetManifestResourceStream("Questionable.QuestPaths.QuestSchema")!; typeof(AssemblyQuestLoader).Assembly.GetManifestResourceStream("Questionable.QuestPaths.QuestSchema")!;
private static void AddQuest(ushort questId, QuestRoot root) => _quests![questId] = root;
} }

View File

@ -9,13 +9,12 @@
<DebugType>none</DebugType> <DebugType>none</DebugType>
<PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap> <PathMap Condition="$(SolutionDir) != ''">$(SolutionDir)=X:\</PathMap>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<Platforms>x64</Platforms>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" /> <ProjectReference Include="..\Questionable.Model\Questionable.Model.csproj" />
<ProjectReference Include="..\QuestPathGenerator\QuestPathGenerator.csproj" <ProjectReference Include="..\QuestPathGenerator\QuestPathGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@ -1,46 +1,48 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 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 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 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 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 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 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 EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|x64.ActiveCfg = ExportRelease|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|Any CPU.Build.0 = Debug|Any CPU {C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Debug|x64.Build.0 = ExportRelease|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.ActiveCfg = Release|Any CPU {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|x64.ActiveCfg = ExportRelease|x64
{C91EEF13-A1AC-4A40-B695-DD4E378E5989}.Release|Any CPU.Build.0 = Release|Any CPU {EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|x64.Build.0 = ExportRelease|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|x64.ActiveCfg = Debug|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|x64.Build.0 = Debug|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.ActiveCfg = Release|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|x64.ActiveCfg = Release|x64
{EEDE3BBE-E260-445E-8FB3-1264E0CBBE91}.Release|Any CPU.Build.0 = Release|Any CPU {7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|x64.Build.0 = Release|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|x64.ActiveCfg = Debug|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|x64.Build.0 = Debug|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|x64.ActiveCfg = Release|x64
{7A136F28-8D5C-478D-B993-0F39F1451A47}.Release|Any CPU.Build.0 = Release|Any CPU {DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|x64.Build.0 = Release|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|x64.ActiveCfg = Debug|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Debug|Any CPU.Build.0 = Debug|Any CPU {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|x64.Build.0 = Debug|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.ActiveCfg = Release|Any CPU {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|x64.ActiveCfg = Release|x64
{DFFD56A8-FA89-4585-A47B-C6AB27B65F0F}.Release|Any CPU.Build.0 = Release|Any CPU {E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|x64.Build.0 = Release|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|x64.ActiveCfg = Debug|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Debug|Any CPU.Build.0 = Debug|Any CPU {4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|x64.Build.0 = Debug|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.ActiveCfg = Release|Any CPU {4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|x64.ActiveCfg = Release|x64
{E15144A5-AFF5-4D86-9561-AFF7DF7F505D}.Release|Any CPU.Build.0 = Release|Any CPU {4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|x64.Build.0 = Release|x64
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU EndGlobalSection
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU GlobalSection(SolutionProperties) = preSolution
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU HideSolutionNode = FALSE
{4FD6F346-8961-4BD5-BDA2-E5F426DE4FC7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

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

View File

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

View File

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

View File

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