#define RELEASE using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.Json; using Dalamud.Plugin; using Microsoft.Extensions.Logging; using Questionable.FatePaths; using Questionable.Model.Questing; using Questionable.Windows.QuestComponents; namespace Questionable.Controller; internal sealed class FateDefinitionRegistry { private readonly IDalamudPluginInterface _pluginInterface; private readonly ILogger _logger; private readonly Dictionary _definitions = new Dictionary(); public IReadOnlyDictionary Definitions => _definitions; public FateDefinitionRegistry(IDalamudPluginInterface pluginInterface, ILogger logger) { _pluginInterface = pluginInterface; _logger = logger; Reload(); } public void Reload() { _definitions.Clear(); LoadFromAssembly(); try { LoadFromDirectory(new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "FateDefinitions"))); } catch (Exception exception) { _logger.LogError(exception, "Failed to load FATE definitions from user directory (some may have been successfully loaded)"); } RemoveExpiredDefinitions(); _logger.LogInformation("Loaded {Count} FATE definitions in total", _definitions.Count); } [Conditional("RELEASE")] private void LoadFromAssembly() { _logger.LogInformation("Loading FATE definitions from assembly"); IReadOnlyDictionary definitions = AssemblyFateDefinitionLoader.GetDefinitions(); _logger.LogInformation("AssemblyFateDefinitionLoader returned {Count} definitions", definitions.Count); foreach (var (key, value) in definitions) { _definitions[key] = value; } _logger.LogInformation("Loaded {Count} FATE definitions from assembly", _definitions.Count); } [Conditional("DEBUG")] private void LoadFromProjectDirectory() { DirectoryInfo directoryInfo = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent; if (directoryInfo == null) { return; } DirectoryInfo directoryInfo2 = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "FatePaths")); if (directoryInfo2.Exists) { try { LoadFromDirectory(directoryInfo2); } catch (Exception exception) { _definitions.Clear(); _logger.LogError(exception, "Failed to load FATE definitions from project directory"); } } } private void LoadFromDirectory(DirectoryInfo directory) { if (!directory.Exists) { _logger.LogInformation("Not loading FATE definitions from {DirectoryName} (doesn't exist)", directory); return; } FileInfo[] files = directory.GetFiles("*.json"); foreach (FileInfo fileInfo in files) { try { ushort? num = ExtractIdFromName(fileInfo.Name); if (!num.HasValue) { continue; } using FileStream utf8Json = new FileStream(fileInfo.FullName, FileMode.Open, FileAccess.Read); FateDefinition fateDefinition = JsonSerializer.Deserialize(utf8Json); if (fateDefinition != null) { _definitions[num.Value] = fateDefinition; } } catch (Exception exception) { _logger.LogError(exception, "Unable to load FATE definition file {FileName}", fileInfo.FullName); } } } private void RemoveExpiredDefinitions() { foreach (ushort item in (from kvp in _definitions.Where>(delegate(KeyValuePair kvp) { DateTime? eventExpiry = kvp.Value.EventExpiry; if (eventExpiry.HasValue) { DateTime valueOrDefault = eventExpiry.GetValueOrDefault(); return EventInfoComponent.NormalizeExpiry(valueOrDefault) < DateTime.UtcNow; } return false; }) select kvp.Key).ToList()) { _logger.LogInformation("Removing expired FATE definition {Id} '{Name}'", item, _definitions[item].Name); _definitions.Remove(item); } } private static ushort? ExtractIdFromName(string fileName) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); if (!fileNameWithoutExtension.Contains('_', StringComparison.Ordinal)) { return null; } if (ushort.TryParse(fileNameWithoutExtension.Split('_', 2)[0], out var result)) { return result; } return null; } }