muffin v6.12

This commit is contained in:
alydev 2025-10-09 07:53:51 +10:00
parent cfb4dea47e
commit c8197297b2
58 changed files with 40038 additions and 58059 deletions

View file

@ -3,10 +3,12 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.RegularExpressions;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using Dalamud.Plugin.Services;
@ -45,6 +47,8 @@ internal sealed class QuestRegistry
private readonly List<(uint ContentFinderConditionId, ElementId QuestId, int Sequence)> _lowPriorityContentFinderConditionQuests = new List<(uint, ElementId, int)>();
private readonly Dictionary<ElementId, string> _questFolderNames = new Dictionary<ElementId, string>();
public IEnumerable<Quest> AllQuests => _quests.Values;
public int Count => _quests.Count<KeyValuePair<ElementId, Quest>>((KeyValuePair<ElementId, Quest> x) => !x.Value.Root.Disabled);
@ -75,6 +79,7 @@ internal sealed class QuestRegistry
_quests.Clear();
_contentFinderConditionIds.Clear();
_lowPriorityContentFinderConditionQuests.Clear();
_questFolderNames.Clear();
LoadQuestsFromAssembly();
try
{
@ -102,19 +107,69 @@ internal sealed class QuestRegistry
private void LoadQuestsFromAssembly()
{
_logger.LogInformation("Loading quests from assembly");
foreach (var (elementId2, root) in AssemblyQuestLoader.GetQuests())
foreach (var (elementId2, questRoot2) in AssemblyQuestLoader.GetQuests())
{
try
{
IQuestInfo questInfo = _questData.GetQuestInfo(elementId2);
bool? flag = null;
DateTime? dateTime = null;
bool flag2 = false;
bool flag3 = false;
try
{
flag = questRoot2.IsSeasonalQuest;
flag2 = flag.HasValue;
if (questRoot2.SeasonalQuestExpiry.HasValue)
{
dateTime = DateTime.SpecifyKind(questRoot2.SeasonalQuestExpiry.Value, DateTimeKind.Utc);
flag3 = true;
}
}
catch (Exception exception)
{
_logger.LogWarning(exception, "Failed to read seasonal fields from embedded QuestRoot for {QuestId}", elementId2);
}
if (_questData.TryGetQuestInfo(elementId2, out IQuestInfo questInfo))
{
goto IL_01c8;
}
if (elementId2 is UnlockLinkId unlockLinkId)
{
string text = unlockLinkId.ToString();
if (text.Length > 1 && text.StartsWith('U'))
{
string text2 = text.Substring(1);
string text3 = ((text2 == "568") ? "Patch 7.3 Fantasia" : ((!(text2 == "506")) ? ("U" + text2) : "Patch 7.2 Fantasia"));
text = text3;
}
else
{
text = $"Unlock Link {unlockLinkId.Value}";
}
questInfo = new UnlockLinkQuestInfo(unlockLinkId, text, 0u, dateTime);
_logger.LogDebug("Created UnlockLinkQuestInfo for {QuestId} from assembly", elementId2);
_questData.AddOrReplaceQuestInfo(questInfo);
goto IL_01c8;
}
_logger.LogWarning("Not loading unknown quest {QuestId} from assembly: Quest not found in quest data", elementId2);
goto end_IL_003d;
IL_01c8:
if (flag2 || flag3)
{
bool flag4 = flag ?? questInfo.IsSeasonalQuest;
_questData.ApplySeasonalOverride(elementId2, flag4, dateTime);
_logger.LogDebug("Applied seasonal override for quest {QuestId} from assembly: IsSeasonal={IsSeasonal}, Expiry={Expiry}", elementId2, flag4, dateTime?.ToString("o") ?? "(null)");
}
IQuestInfo questInfo2 = _questData.GetQuestInfo(elementId2);
Quest quest = new Quest
{
Id = elementId2,
Root = root,
Info = questInfo,
Root = questRoot2,
Info = questInfo2,
Source = Quest.ESource.Assembly
};
_quests[quest.Id] = quest;
end_IL_003d:;
}
catch (Exception ex)
{
@ -191,27 +246,151 @@ internal sealed class QuestRegistry
_questValidator.Validate(_quests.Values.Where((Quest x) => x.Source != Quest.ESource.Assembly).ToList());
}
private void LoadQuestFromStream(string fileName, Stream stream, Quest.ESource source)
private void LoadQuestFromStream(string fileName, Stream stream, Quest.ESource source, string directoryName)
{
if (source == Quest.ESource.UserDirectory)
{
_logger.LogTrace("Loading quest from '{FileName}'", fileName);
}
ElementId elementId = ExtractQuestIdFromName(fileName);
if (!(elementId == null))
if (elementId == null)
{
JsonNode jsonNode = JsonNode.Parse(stream);
_jsonSchemaValidator.Enqueue(elementId, jsonNode);
QuestRoot root = jsonNode.Deserialize<QuestRoot>();
IQuestInfo questInfo = _questData.GetQuestInfo(elementId);
Quest quest = new Quest
return;
}
JsonNode jsonNode;
try
{
jsonNode = JsonNode.Parse(stream);
}
catch (JsonException ex)
{
ValidationIssue issue = new ValidationIssue
{
Id = elementId,
Root = root,
Info = questInfo,
Source = source
ElementId = elementId,
Sequence = null,
Step = null,
Type = EIssueType.InvalidJsonSyntax,
Severity = EIssueSeverity.Error,
Description = $"JSON parsing error in file '{fileName}': {ex.Message}\n\nThis usually indicates a syntax error such as:\n\ufffd Missing comma between properties\n\ufffd Unclosed quotes or brackets\n\ufffd Invalid escape sequences\n\ufffd Trailing commas where not allowed\n\nPlease check the JSON syntax around the indicated position."
};
_quests[quest.Id] = quest;
_questValidator.AddValidationIssue(issue);
return;
}
_jsonSchemaValidator.Enqueue(elementId, jsonNode);
bool? flag = null;
DateTime? dateTime = null;
bool flag2 = false;
bool flag3 = false;
if (jsonNode is JsonObject jsonObject)
{
if (jsonObject.TryGetPropertyValue("IsSeasonalQuest", out JsonNode jsonNode2) && jsonNode2 != null)
{
try
{
flag = jsonNode2.GetValue<bool>();
flag2 = true;
_logger.LogDebug("Quest {QuestId}: parsed IsSeasonalQuest override = {IsSeasonal}", elementId, flag);
}
catch (Exception exception)
{
_logger.LogWarning(exception, "Quest {QuestId}: failed to parse IsSeasonalQuest from JSON", elementId);
}
}
if (jsonObject.TryGetPropertyValue("SeasonalQuestExpiry", out JsonNode jsonNode3) && jsonNode3 != null)
{
try
{
string value = jsonNode3.GetValue<string>();
if (!string.IsNullOrEmpty(value))
{
dateTime = ((!DateTime.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result)) ? new DateTime?(DateTime.Parse(value, null, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal)) : new DateTime?(DateTime.SpecifyKind(result, DateTimeKind.Utc)));
flag3 = true;
_logger.LogDebug("Quest {QuestId}: parsed SeasonalQuestExpiry override = {Expiry}", elementId, dateTime);
}
}
catch (Exception exception2)
{
_logger.LogWarning(exception2, "Quest {QuestId}: failed to parse SeasonalQuestExpiry from JSON", elementId);
}
}
}
QuestRoot root = jsonNode.Deserialize<QuestRoot>();
if (!_questData.TryGetQuestInfo(elementId, out IQuestInfo questInfo))
{
if (!(elementId is UnlockLinkId unlockLinkId))
{
_logger.LogWarning("Not loading unknown quest {QuestId} from project file {FileName}", elementId, fileName);
return;
}
string name;
try
{
string text = fileName.Substring(0, fileName.Length - ".json".Length);
int num = text.IndexOf('_', StringComparison.Ordinal);
string text2;
if (num < 0 || num + 1 >= text.Length)
{
text2 = text;
}
else
{
string text3 = text;
int num2 = num + 1;
text2 = text3.Substring(num2, text3.Length - num2);
}
name = text2;
}
catch
{
name = fileName.Substring(0, fileName.Length - ".json".Length);
}
name = NormalizeDerivedName(name);
uint issuerDataId = 0u;
string patch = null;
if (jsonNode is JsonObject jsonObject2)
{
if (jsonObject2.TryGetPropertyValue("DataId", out JsonNode jsonNode4) && jsonNode4 != null)
{
try
{
issuerDataId = jsonNode4.GetValue<uint>();
}
catch
{
issuerDataId = 0u;
}
}
if (jsonObject2.TryGetPropertyValue("Patch", out JsonNode jsonNode5) && jsonNode5 != null)
{
try
{
patch = jsonNode5.GetValue<string>();
}
catch
{
patch = null;
}
}
}
questInfo = new UnlockLinkQuestInfo(unlockLinkId, name, issuerDataId, dateTime, patch);
_logger.LogDebug("Created UnlockLinkQuestInfo for {QuestId} from project file '{FileName}'", elementId, fileName);
_questData.AddOrReplaceQuestInfo(questInfo);
}
if ((flag2 || flag3) && _questData.TryGetQuestInfo(elementId, out IQuestInfo questInfo2))
{
_questData.ApplySeasonalOverride(elementId, flag ?? questInfo2.IsSeasonalQuest, dateTime);
}
Quest quest = new Quest
{
Id = elementId,
Root = root,
Info = questInfo,
Source = source
};
_quests[quest.Id] = quest;
if (!string.IsNullOrEmpty(directoryName))
{
_questFolderNames[elementId] = directoryName;
}
}
@ -232,7 +411,7 @@ internal sealed class QuestRegistry
try
{
using FileStream stream = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read);
LoadQuestFromStream(fileInfo.Name, stream, source);
LoadQuestFromStream(fileInfo.Name, stream, source, directory.Name);
}
catch (Exception innerException)
{
@ -287,4 +466,26 @@ internal sealed class QuestRegistry
dutyOptions = null;
return false;
}
public IEnumerable<ElementId> GetAllQuestIds()
{
return _quests.Keys;
}
public bool TryGetQuestFolderName(ElementId questId, [NotNullWhen(true)] out string? folderName)
{
return _questFolderNames.TryGetValue(questId, out folderName);
}
private static string NormalizeDerivedName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return name ?? string.Empty;
}
name = name.Replace("_", " ", StringComparison.OrdinalIgnoreCase);
name = Regex.Replace(name, "\\s+", " ");
name = Regex.Replace(name, "\\b(Patch)\\s+(\\d+)\\s+(\\d+)\\b", "$1 $2.$3", RegexOptions.IgnoreCase);
return name;
}
}