210 lines
7.2 KiB
C#
210 lines
7.2 KiB
C#
#define RELEASE
|
|
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 Dalamud.Plugin;
|
|
using LLib.GameData;
|
|
using Microsoft.Extensions.Logging;
|
|
using Questionable.Data;
|
|
using Questionable.GatheringPaths;
|
|
using Questionable.Model;
|
|
using Questionable.Model.Gathering;
|
|
using Questionable.Validation;
|
|
using Questionable.Validation.Validators;
|
|
|
|
namespace Questionable.Controller;
|
|
|
|
internal sealed class GatheringPointRegistry
|
|
{
|
|
private readonly IDalamudPluginInterface _pluginInterface;
|
|
|
|
private readonly GatheringData _gatheringData;
|
|
|
|
private readonly GatheringSchemaValidator _gatheringSchemaValidator;
|
|
|
|
private readonly QuestValidator _questValidator;
|
|
|
|
private readonly ILogger<GatheringPointRegistry> _logger;
|
|
|
|
private volatile Dictionary<GatheringPointId, GatheringRoot> _gatheringPoints = new Dictionary<GatheringPointId, GatheringRoot>();
|
|
|
|
public GatheringPointRegistry(IDalamudPluginInterface pluginInterface, GatheringData gatheringData, GatheringSchemaValidator gatheringSchemaValidator, QuestValidator questValidator, ILogger<GatheringPointRegistry> logger)
|
|
{
|
|
_pluginInterface = pluginInterface;
|
|
_gatheringData = gatheringData;
|
|
_gatheringSchemaValidator = gatheringSchemaValidator;
|
|
_questValidator = questValidator;
|
|
_logger = logger;
|
|
}
|
|
|
|
public Dictionary<GatheringPointId, GatheringRoot> Build()
|
|
{
|
|
Dictionary<GatheringPointId, GatheringRoot> dictionary = new Dictionary<GatheringPointId, GatheringRoot>();
|
|
_gatheringSchemaValidator.Reset();
|
|
LoadGatheringPointsFromAssembly(dictionary);
|
|
try
|
|
{
|
|
LoadFromDirectory(dictionary, new DirectoryInfo(Path.Combine(_pluginInterface.ConfigDirectory.FullName, "GatheringPoints")));
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
_logger.LogError(exception, "Failed to load gathering points from user directory (some may have been successfully loaded)");
|
|
}
|
|
foreach (ValidationIssue item in _gatheringSchemaValidator.Validate())
|
|
{
|
|
_questValidator.AddValidationIssue(item);
|
|
}
|
|
_logger.LogDebug("Loaded {Count} gathering points in total", dictionary.Count);
|
|
return dictionary;
|
|
}
|
|
|
|
public void Publish(Dictionary<GatheringPointId, GatheringRoot> gatheringPoints)
|
|
{
|
|
_gatheringPoints = gatheringPoints;
|
|
}
|
|
|
|
public void Reload()
|
|
{
|
|
Publish(Build());
|
|
}
|
|
|
|
[Conditional("RELEASE")]
|
|
private void LoadGatheringPointsFromAssembly(Dictionary<GatheringPointId, GatheringRoot> gatheringPoints)
|
|
{
|
|
foreach (var (value, value2) in AssemblyGatheringLocationLoader.GetLocations())
|
|
{
|
|
gatheringPoints[new GatheringPointId(value)] = value2;
|
|
}
|
|
_logger.LogDebug("Loaded {Count} gathering points from assembly", gatheringPoints.Count);
|
|
}
|
|
|
|
[Conditional("DEBUG")]
|
|
private void LoadGatheringPointsFromProjectDirectory(Dictionary<GatheringPointId, GatheringRoot> gatheringPoints)
|
|
{
|
|
DirectoryInfo directoryInfo = _pluginInterface.AssemblyLocation.Directory?.Parent?.Parent;
|
|
if (directoryInfo == null)
|
|
{
|
|
_logger.LogWarning("Could not determine solution directory from assembly location: {AssemblyLocation}", _pluginInterface.AssemblyLocation.FullName);
|
|
return;
|
|
}
|
|
DirectoryInfo directoryInfo2 = new DirectoryInfo(Path.Combine(directoryInfo.FullName, "Paths", "GatheringPaths"));
|
|
try
|
|
{
|
|
foreach (string value in ExpansionData.ExpansionFolders.Values)
|
|
{
|
|
LoadFromDirectory(gatheringPoints, new DirectoryInfo(Path.Combine(directoryInfo2.FullName, value)));
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
gatheringPoints.Clear();
|
|
_logger.LogError(exception, "Failed to load gathering points from project directory");
|
|
}
|
|
}
|
|
|
|
private void LoadGatheringPointFromStream(Dictionary<GatheringPointId, GatheringRoot> gatheringPoints, string fileName, Stream stream)
|
|
{
|
|
GatheringPointId gatheringPointId = ExtractGatheringPointIdFromName(fileName);
|
|
if (!(gatheringPointId == null))
|
|
{
|
|
JsonNode jsonNode = JsonNode.Parse(stream);
|
|
if (jsonNode != null)
|
|
{
|
|
_gatheringSchemaValidator.Enqueue(gatheringPointId.Value, jsonNode);
|
|
gatheringPoints[gatheringPointId] = jsonNode.Deserialize<GatheringRoot>();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LoadFromDirectory(Dictionary<GatheringPointId, GatheringRoot> gatheringPoints, DirectoryInfo directory)
|
|
{
|
|
if (!directory.Exists)
|
|
{
|
|
_logger.LogDebug("Not loading gathering points from {DirectoryName} (doesn't exist)", directory);
|
|
return;
|
|
}
|
|
FileInfo[] files = directory.GetFiles("*.json");
|
|
foreach (FileInfo fileInfo in files)
|
|
{
|
|
try
|
|
{
|
|
using FileStream stream = fileInfo.OpenRead();
|
|
LoadGatheringPointFromStream(gatheringPoints, fileInfo.Name, stream);
|
|
}
|
|
catch (JsonException ex)
|
|
{
|
|
GatheringPointId gatheringPointId = ExtractGatheringPointIdFromName(fileInfo.Name);
|
|
_logger.LogError(ex, "Unable to parse gathering point file {FileName}", fileInfo.FullName);
|
|
_questValidator.AddValidationIssue(new ValidationIssue
|
|
{
|
|
PathType = EPathType.Gathering,
|
|
Id = gatheringPointId?.Value.ToString(CultureInfo.InvariantCulture),
|
|
Location = null,
|
|
Type = EIssueType.InvalidJsonSyntax,
|
|
Severity = EIssueSeverity.Error,
|
|
Description = "JSON syntax error in " + fileInfo.Name + ": " + ex.Message
|
|
});
|
|
}
|
|
catch (Exception innerException)
|
|
{
|
|
throw new InvalidDataException("Unable to load file " + fileInfo.FullName, innerException);
|
|
}
|
|
}
|
|
DirectoryInfo[] directories = directory.GetDirectories();
|
|
foreach (DirectoryInfo directory2 in directories)
|
|
{
|
|
LoadFromDirectory(gatheringPoints, directory2);
|
|
}
|
|
}
|
|
|
|
private static GatheringPointId? ExtractGatheringPointIdFromName(string resourceName)
|
|
{
|
|
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(resourceName);
|
|
fileNameWithoutExtension = fileNameWithoutExtension.Substring(fileNameWithoutExtension.LastIndexOf('.') + 1);
|
|
if (!fileNameWithoutExtension.Contains('_', StringComparison.Ordinal))
|
|
{
|
|
return null;
|
|
}
|
|
return GatheringPointId.FromString(fileNameWithoutExtension.Split('_', 2)[0]);
|
|
}
|
|
|
|
public bool TryGetGatheringPoint(GatheringPointId gatheringPointId, [NotNullWhen(true)] out GatheringRoot? gatheringRoot)
|
|
{
|
|
return _gatheringPoints.TryGetValue(gatheringPointId, out gatheringRoot);
|
|
}
|
|
|
|
public bool TryGetGatheringPointId(uint itemId, EClassJob classJobId, [NotNullWhen(true)] out GatheringPointId? gatheringPointId)
|
|
{
|
|
Dictionary<GatheringPointId, GatheringRoot> gatheringPoints = _gatheringPoints;
|
|
switch (classJobId)
|
|
{
|
|
case EClassJob.Miner:
|
|
if (_gatheringData.TryGetMinerGatheringPointByItemId(itemId, out gatheringPointId))
|
|
{
|
|
return true;
|
|
}
|
|
gatheringPointId = (from x in gatheringPoints
|
|
where x.Value.ExtraQuestItems.Contains(itemId)
|
|
select x.Key).FirstOrDefault((GatheringPointId x) => _gatheringData.MinerGatheringPoints.Contains(x));
|
|
return gatheringPointId != null;
|
|
case EClassJob.Botanist:
|
|
if (_gatheringData.TryGetBotanistGatheringPointByItemId(itemId, out gatheringPointId))
|
|
{
|
|
return true;
|
|
}
|
|
gatheringPointId = (from x in gatheringPoints
|
|
where x.Value.ExtraQuestItems.Contains(itemId)
|
|
select x.Key).FirstOrDefault((GatheringPointId x) => _gatheringData.BotanistGatheringPoints.Contains(x));
|
|
return gatheringPointId != null;
|
|
default:
|
|
gatheringPointId = null;
|
|
return false;
|
|
}
|
|
}
|
|
}
|