diff --git a/QuestPathGenerator.Tests/QuestPathGenerator.Tests.csproj b/QuestPathGenerator.Tests/QuestPathGenerator.Tests.csproj index 98a27c97c..a6c43a4f9 100644 --- a/QuestPathGenerator.Tests/QuestPathGenerator.Tests.csproj +++ b/QuestPathGenerator.Tests/QuestPathGenerator.Tests.csproj @@ -7,13 +7,14 @@ false true + x64 - - - - + + + + diff --git a/QuestPathGenerator/QuestPathGenerator.csproj b/QuestPathGenerator/QuestPathGenerator.csproj index 8ee14a8f2..d9b119d4d 100644 --- a/QuestPathGenerator/QuestPathGenerator.csproj +++ b/QuestPathGenerator/QuestPathGenerator.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 false @@ -13,6 +13,7 @@ QuestPathGenerator true + x64 @@ -23,8 +24,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/QuestPathGenerator/QuestSourceGenerator.cs b/QuestPathGenerator/QuestSourceGenerator.cs index 02fc2715f..2e2d75303 100644 --- a/QuestPathGenerator/QuestSourceGenerator.cs +++ b/QuestPathGenerator/QuestSourceGenerator.cs @@ -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 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,116 +168,61 @@ public class QuestSourceGenerator : ISourceGenerator SingletonList( ClassDeclaration("AssemblyQuestLoader") .WithModifiers( - TokenList( - [ - Token(SyntaxKind.PartialKeyword) - ])) - .WithMembers( - SingletonList( - FieldDeclaration( - VariableDeclaration( - GenericName( - Identifier("IReadOnlyDictionary")) - .WithTypeArgumentList( - TypeArgumentList( - SeparatedList( - 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(methods)))))) .NormalizeWhitespace(); // Add the source code to the compilation. context.AddSource("AssemblyQuestLoader.g.cs", code.ToFullString()); } - private static IEnumerable CreateQuestInitializer(ushort questId, QuestRoot quest) + private static StatementSyntax[] CreateInitializer(List<(ushort QuestId, QuestRoot Root)> quests) + { + List statements = []; + + foreach (var quest in quests) + { + statements.Add( + ExpressionStatement( + InvocationExpression( + IdentifierName("AddQuest")) + .WithArgumentList( + ArgumentList( + SeparatedList( + 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( - new SyntaxNodeOrToken[] - { - LiteralExpression( - SyntaxKind.NumericLiteralExpression, - Literal(questId)), - Token(SyntaxKind.CommaToken), - ObjectCreationExpression( - IdentifierName(nameof(QuestRoot))) - .WithInitializer( - InitializerExpression( - SyntaxKind.ObjectInitializerExpression, - SeparatedList( - SyntaxNodeList( - AssignmentList(nameof(QuestRoot.Author), quest.Author) - .AsSyntaxNodeOrToken(), - Assignment(nameof(QuestRoot.Comment), quest.Comment, null) - .AsSyntaxNodeOrToken(), - AssignmentList(nameof(QuestRoot.TerritoryBlacklist), - quest.TerritoryBlacklist).AsSyntaxNodeOrToken(), - AssignmentExpression( - SyntaxKind.SimpleAssignmentExpression, - IdentifierName(nameof(QuestRoot.QuestSequence)), - CreateQuestSequence(quest.QuestSequence)) - )))) - })), - Token(SyntaxKind.CommaToken) - }; + return ObjectCreationExpression( + IdentifierName(nameof(QuestRoot))) + .WithInitializer( + InitializerExpression( + SyntaxKind.ObjectInitializerExpression, + SeparatedList( + SyntaxNodeList( + AssignmentList(nameof(QuestRoot.Author), quest.Author) + .AsSyntaxNodeOrToken(), + Assignment(nameof(QuestRoot.Comment), quest.Comment, null) + .AsSyntaxNodeOrToken(), + AssignmentList(nameof(QuestRoot.TerritoryBlacklist), + quest.TerritoryBlacklist).AsSyntaxNodeOrToken(), + AssignmentExpression( + SyntaxKind.SimpleAssignmentExpression, + IdentifierName(nameof(QuestRoot.QuestSequence)), + CreateQuestSequence(quest.QuestSequence)))))); } catch (Exception e) { diff --git a/QuestPaths/AssemblyQuestLoader.cs b/QuestPaths/AssemblyQuestLoader.cs index 086e4ad9b..afc7ea83e 100644 --- a/QuestPaths/AssemblyQuestLoader.cs +++ b/QuestPaths/AssemblyQuestLoader.cs @@ -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 GetQuests() => + private static Dictionary? _quests; + + public static IReadOnlyDictionary GetQuests() + { + if (_quests == null) + { + _quests = []; #if RELEASE - Quests; -#else - new Dictionary(); + 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; } diff --git a/QuestPaths/QuestPaths.csproj b/QuestPaths/QuestPaths.csproj index 6d9e7d785..bb2b3663f 100644 --- a/QuestPaths/QuestPaths.csproj +++ b/QuestPaths/QuestPaths.csproj @@ -9,35 +9,34 @@ none $(SolutionDir)=X:\ true + x64 - - + + - + Questionable.QuestPaths.QuestSchema - + - - - - - - - - - - + + + + + + + + + + - + diff --git a/Questionable.Model/Questionable.Model.csproj b/Questionable.Model/Questionable.Model.csproj index 8909f97e2..cb320ec55 100644 --- a/Questionable.Model/Questionable.Model.csproj +++ b/Questionable.Model/Questionable.Model.csproj @@ -7,6 +7,7 @@ $(SolutionDir)=X:\ true portable + x64 diff --git a/Questionable.sln b/Questionable.sln index 0e614b410..5dd759643 100644 --- a/Questionable.sln +++ b/Questionable.sln @@ -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 diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs index b255a34ac..62f3a9359 100644 --- a/Questionable/Controller/QuestController.cs +++ b/Questionable/Controller/QuestController.cs @@ -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; } diff --git a/Questionable/Controller/QuestRegistry.cs b/Questionable/Controller/QuestRegistry.cs index a7b83b7f5..a584e776c 100644 --- a/Questionable/Controller/QuestRegistry.cs +++ b/Questionable/Controller/QuestRegistry.cs @@ -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")] diff --git a/Questionable/Validation/QuestValidator.cs b/Questionable/Validation/QuestValidator.cs index b5b7bd4f6..d53a5b52c 100644 --- a/Questionable/Validation/QuestValidator.cs +++ b/Questionable/Validation/QuestValidator.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -38,26 +39,35 @@ internal sealed class QuestValidator { Task.Factory.StartNew(() => { - foreach (var quest in quests) + try { - foreach (var validator in _validators) + foreach (var quest in quests) { - foreach (var issue in validator.Validate(quest)) + foreach (var validator in _validators) { - 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); - _validationIssues.Add(issue); + foreach (var issue in validator.Validate(quest)) + { + 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); + _validationIssues.Add(issue); + } } } - } - _validationIssues = _validationIssues.OrderBy(x => x.QuestId) - .ThenBy(x => x.Sequence) - .ThenBy(x => x.Step) - .ThenBy(x => x.Description) - .ToList(); + _validationIssues = _validationIssues.OrderBy(x => x.QuestId) + .ThenBy(x => x.Sequence) + .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); } } diff --git a/Questionable/Windows/QuestSelectionWindow.cs b/Questionable/Windows/QuestSelectionWindow.cs index 9d48d69a9..592e2b82c 100644 --- a/Questionable/Windows/QuestSelectionWindow.cs +++ b/Questionable/Windows/QuestSelectionWindow.cs @@ -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);