using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Questionable.Model; using Questionable.Model.Questing; using Questionable.Validation.Validators; namespace Questionable.Validation; internal sealed class QuestValidator { private readonly IReadOnlyList _validators; private readonly ILogger _logger; private readonly object _lock = new object(); private List _validationIssues = new List(); public IReadOnlyList Issues => _validationIssues; public int IssueCount => _validationIssues.Count; public int ErrorCount => _validationIssues.Count((ValidationIssue x) => x.Severity == EIssueSeverity.Error); public int WarningCount => _validationIssues.Count((ValidationIssue x) => x.Severity == EIssueSeverity.Warning); public QuestValidator(IEnumerable validators, ILogger logger) { _validators = validators.ToList(); _logger = logger; _logger.LogDebug("Validators: {Validators}", string.Join(", ", _validators.Select((IQuestValidator x) => x.GetType().Name))); } public void Reset() { foreach (IQuestValidator validator in _validators) { validator.Reset(); } lock (_lock) { _validationIssues = new List(); } } public void AddValidationIssue(ValidationIssue issue) { lock (_lock) { List validationIssues = _validationIssues; int num = 1 + validationIssues.Count; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); int num2 = 0; Span span2 = CollectionsMarshal.AsSpan(validationIssues); span2.CopyTo(span.Slice(num2, span2.Length)); num2 += span2.Length; span[num2] = issue; _validationIssues = list; } } public void Validate(IEnumerable quests) { Task.Factory.StartNew(delegate { try { List validationIssues; lock (_lock) { validationIssues = _validationIssues; } List list = new List(); Dictionary> dictionary = new Dictionary>(); foreach (Quest quest in quests) { foreach (IQuestValidator validator in _validators) { foreach (ValidationIssue item in validator.Validate(quest)) { if (item.Type == EIssueType.QuestDisabled && quest.Info.AlliedSociety != EAlliedSociety.None) { if (!dictionary.TryGetValue(quest.Info.AlliedSociety, out var value)) { value = new List(); dictionary[quest.Info.AlliedSociety] = value; } value.Add(quest.Id); } else { list.Add(item); } } } } List disabledQuests = (from x in list where x.Type == EIssueType.QuestDisabled select x.Id).ToList(); List list2 = (from x in validationIssues.Concat(list.Where((ValidationIssue x) => !disabledQuests.Contains(x.Id) || x.Type == EIssueType.QuestDisabled)).Concat(DisabledTribesAsIssues(dictionary)) orderby x.PathType, x.Id, x.Location, x.Description select x).ToList(); lock (_lock) { if (_validationIssues.Count > validationIssues.Count) { list2.AddRange(_validationIssues.Skip(validationIssues.Count)); } _validationIssues = list2; } } catch (Exception exception) { _logger.LogError(exception, "Unable to validate quests"); } }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); } public List GetIssues(EPathType pathType, string id) { return _validationIssues.Where((ValidationIssue x) => x.PathType == pathType && x.Id == id).ToList(); } private static IEnumerable DisabledTribesAsIssues(Dictionary> disabledTribeQuests) { return disabledTribeQuests.OrderBy>, EAlliedSociety>((KeyValuePair> x) => x.Key).Select(delegate(KeyValuePair> x) { string value = ((x.Value.Count > 0) ? string.Join(", ", x.Value.Select((ElementId id) => id.ToString())) : "(none)"); return new ValidationIssue { PathType = EPathType.Quest, Id = null, Location = null, Type = EIssueType.QuestDisabled, Severity = EIssueSeverity.None, Description = $"[{x.Key}] {x.Value.Count} disabled {((x.Value.Count == 1) ? "quest" : "quests")}: {value}" }; }); } public List ValidateQuestSync(Quest quest) { List list = new List(); foreach (IQuestValidator validator in _validators) { foreach (ValidationIssue item in (validator is JsonSchemaValidator jsonSchemaValidator) ? jsonSchemaValidator.ValidateFromQuestRoot(quest) : validator.Validate(quest)) { list.Add(item); } } return (from x in list orderby x.Sequence, x.Step, x.Description select x).ToList(); } public List ValidateSchemaSync(ElementId questId, QuestRoot questRoot) { JsonSchemaValidator jsonSchemaValidator = _validators.OfType().FirstOrDefault(); if (jsonSchemaValidator == null) { return new List(); } return (from x in jsonSchemaValidator.ValidateFromQuestRoot(questId, questRoot) orderby x.Sequence, x.Step, x.Description select x).ToList(); } public void ClearIssuesForQuest(ElementId questId) { lock (_lock) { _validationIssues = _validationIssues.Where((ValidationIssue issue) => issue.ElementId != questId).ToList(); } } }