454 lines
9.8 KiB
C#
454 lines
9.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using Dalamud.Plugin;
|
|
using Dalamud.Plugin.Ipc;
|
|
using Dalamud.Plugin.Services;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
namespace QuestionableCompanion.Services;
|
|
|
|
public class ARPostProcessEventQuestService : IDisposable
|
|
{
|
|
private readonly IDalamudPluginInterface pluginInterface;
|
|
|
|
private readonly QuestionableIPC questionableIPC;
|
|
|
|
private readonly EventQuestResolver eventQuestResolver;
|
|
|
|
private readonly Configuration configuration;
|
|
|
|
private readonly IPluginLog log;
|
|
|
|
private readonly IFramework framework;
|
|
|
|
private readonly ICommandManager commandManager;
|
|
|
|
private readonly LifestreamIPC lifestreamIPC;
|
|
|
|
private ICallGateSubscriber<object>? characterAdditionalTaskSubscriber;
|
|
|
|
private ICallGateSubscriber<string, object>? characterPostProcessSubscriber;
|
|
|
|
private Action? characterAdditionalTaskHandler;
|
|
|
|
private Action<string>? characterPostProcessHandler;
|
|
|
|
private bool isProcessingEventQuests;
|
|
|
|
private DateTime postProcessStartTime;
|
|
|
|
private List<string> currentQuestHierarchy = new List<string>();
|
|
|
|
private string currentPluginName = string.Empty;
|
|
|
|
private string lastTerritoryWaitDetected = string.Empty;
|
|
|
|
private DateTime lastTerritoryTeleportTime = DateTime.MinValue;
|
|
|
|
private const string PLUGIN_NAME = "QuestionableCompanion";
|
|
|
|
private const string AR_CHARACTER_ADDITIONAL_TASK = "AutoRetainer.OnCharacterAdditionalTask";
|
|
|
|
private const string AR_CHARACTER_POST_PROCESS_EVENT = "AutoRetainer.OnCharacterReadyForPostprocess";
|
|
|
|
private const string AR_FINISH_CHARACTER_POST_PROCESS = "AutoRetainer.FinishCharacterPostprocessRequest";
|
|
|
|
private const string AR_REQUEST_CHARACTER_POST_PROCESS = "AutoRetainer.RequestCharacterPostprocess";
|
|
|
|
public ARPostProcessEventQuestService(IDalamudPluginInterface pluginInterface, QuestionableIPC questionableIPC, EventQuestResolver eventQuestResolver, Configuration configuration, IPluginLog log, IFramework framework, ICommandManager commandManager, LifestreamIPC lifestreamIPC)
|
|
{
|
|
this.pluginInterface = pluginInterface;
|
|
this.questionableIPC = questionableIPC;
|
|
this.eventQuestResolver = eventQuestResolver;
|
|
this.configuration = configuration;
|
|
this.log = log;
|
|
this.framework = framework;
|
|
this.commandManager = commandManager;
|
|
this.lifestreamIPC = lifestreamIPC;
|
|
InitializeIPC();
|
|
}
|
|
|
|
private void InitializeIPC()
|
|
{
|
|
try
|
|
{
|
|
characterAdditionalTaskSubscriber = pluginInterface.GetIpcSubscriber<object>("AutoRetainer.OnCharacterAdditionalTask");
|
|
if (characterAdditionalTaskSubscriber == null)
|
|
{
|
|
return;
|
|
}
|
|
characterAdditionalTaskHandler = delegate
|
|
{
|
|
try
|
|
{
|
|
RegisterWithAutoRetainer();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
};
|
|
characterAdditionalTaskSubscriber.Subscribe(characterAdditionalTaskHandler);
|
|
characterPostProcessSubscriber = pluginInterface.GetIpcSubscriber<string, object>("AutoRetainer.OnCharacterReadyForPostprocess");
|
|
if (characterPostProcessSubscriber == null)
|
|
{
|
|
return;
|
|
}
|
|
characterPostProcessHandler = delegate(string pluginName)
|
|
{
|
|
try
|
|
{
|
|
OnARCharacterPostProcessStarted(pluginName);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
};
|
|
characterPostProcessSubscriber.Subscribe(characterPostProcessHandler);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private void RegisterWithAutoRetainer()
|
|
{
|
|
try
|
|
{
|
|
pluginInterface.GetIpcSubscriber<string, object>("AutoRetainer.RequestCharacterPostprocess").InvokeAction("QuestionableCompanion");
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private void OnARCharacterPostProcessStarted(string pluginName)
|
|
{
|
|
try
|
|
{
|
|
if (pluginName != "QuestionableCompanion")
|
|
{
|
|
return;
|
|
}
|
|
if (!configuration.RunEventQuestsOnARPostProcess)
|
|
{
|
|
FinishPostProcess();
|
|
return;
|
|
}
|
|
currentPluginName = pluginName;
|
|
postProcessStartTime = DateTime.Now;
|
|
framework.RunOnFrameworkThread(async delegate
|
|
{
|
|
try
|
|
{
|
|
await ProcessEventQuestsAsync();
|
|
}
|
|
catch
|
|
{
|
|
FinishPostProcess();
|
|
}
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
try
|
|
{
|
|
FinishPostProcess();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task ProcessEventQuestsAsync()
|
|
{
|
|
if (isProcessingEventQuests)
|
|
{
|
|
return;
|
|
}
|
|
isProcessingEventQuests = true;
|
|
bool shouldFinishPostProcess = false;
|
|
try
|
|
{
|
|
_ = 1;
|
|
try
|
|
{
|
|
List<string> detectedEventQuests = DetectActiveEventQuests();
|
|
if (detectedEventQuests.Count == 0)
|
|
{
|
|
shouldFinishPostProcess = true;
|
|
return;
|
|
}
|
|
currentQuestHierarchy = new List<string>(detectedEventQuests);
|
|
await ImportEventQuestsForPostProcess(detectedEventQuests);
|
|
await WaitForEventQuestsCompletion(detectedEventQuests);
|
|
shouldFinishPostProcess = true;
|
|
}
|
|
catch
|
|
{
|
|
shouldFinishPostProcess = true;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (shouldFinishPostProcess)
|
|
{
|
|
await ClearPriorityQuests();
|
|
FinishPostProcess();
|
|
}
|
|
isProcessingEventQuests = false;
|
|
}
|
|
}
|
|
|
|
private List<string> DetectActiveEventQuests()
|
|
{
|
|
try
|
|
{
|
|
return questionableIPC.GetCurrentlyActiveEventQuests() ?? new List<string>();
|
|
}
|
|
catch
|
|
{
|
|
return new List<string>();
|
|
}
|
|
}
|
|
|
|
private async Task ImportEventQuestsForPostProcess(List<string> detectedEventQuests)
|
|
{
|
|
List<string> allQuestsToImport = new List<string>();
|
|
try
|
|
{
|
|
foreach (string questId in detectedEventQuests)
|
|
{
|
|
foreach (string quest in await GetQuestHierarchy(questId))
|
|
{
|
|
if (!allQuestsToImport.Contains(quest))
|
|
{
|
|
allQuestsToImport.Add(quest);
|
|
}
|
|
}
|
|
}
|
|
if (!questionableIPC.IsAvailable)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
questionableIPC.ClearQuestPriority();
|
|
await Task.Delay(500);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
foreach (string questId2 in allQuestsToImport)
|
|
{
|
|
try
|
|
{
|
|
questionableIPC.AddQuestPriority(questId2);
|
|
await Task.Delay(100);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
await Task.Delay(500);
|
|
try
|
|
{
|
|
await framework.RunOnFrameworkThread(delegate
|
|
{
|
|
commandManager.ProcessCommand("/qst start");
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private async Task<List<string>> GetQuestHierarchy(string questId)
|
|
{
|
|
List<string> hierarchy = new List<string>();
|
|
HashSet<string> visited = new HashSet<string>();
|
|
await CollectPrerequisitesRecursive(questId, hierarchy, visited);
|
|
return hierarchy;
|
|
}
|
|
|
|
private async Task CollectPrerequisitesRecursive(string questId, List<string> hierarchy, HashSet<string> visited)
|
|
{
|
|
if (visited.Contains(questId))
|
|
{
|
|
return;
|
|
}
|
|
visited.Add(questId);
|
|
try
|
|
{
|
|
List<string> prerequisites = eventQuestResolver.ResolveEventQuestDependencies(questId);
|
|
if (prerequisites.Count > 0)
|
|
{
|
|
foreach (string prereq in prerequisites)
|
|
{
|
|
await CollectPrerequisitesRecursive(prereq, hierarchy, visited);
|
|
}
|
|
}
|
|
hierarchy.Add(questId);
|
|
}
|
|
catch
|
|
{
|
|
hierarchy.Add(questId);
|
|
}
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
private async Task WaitForEventQuestsCompletion(List<string> originalEventQuests)
|
|
{
|
|
TimeSpan maxWaitTime = TimeSpan.FromMinutes(configuration.EventQuestPostProcessTimeoutMinutes);
|
|
DateTime startTime = DateTime.Now;
|
|
TimeSpan checkInterval = TimeSpan.FromSeconds(2L);
|
|
while (DateTime.Now - startTime < maxWaitTime)
|
|
{
|
|
try
|
|
{
|
|
CheckForTerritoryWait();
|
|
if (!questionableIPC.IsAvailable)
|
|
{
|
|
await Task.Delay(checkInterval);
|
|
continue;
|
|
}
|
|
bool isRunning = questionableIPC.IsRunning();
|
|
List<string> currentEventQuests = DetectActiveEventQuests();
|
|
if (originalEventQuests.Where((string q) => currentEventQuests.Contains(q)).ToList().Count == 0)
|
|
{
|
|
if (!isRunning)
|
|
{
|
|
break;
|
|
}
|
|
try
|
|
{
|
|
await framework.RunOnFrameworkThread(delegate
|
|
{
|
|
commandManager.ProcessCommand("/qst stop");
|
|
});
|
|
await Task.Delay(500);
|
|
break;
|
|
}
|
|
catch
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
await Task.Delay(checkInterval);
|
|
}
|
|
}
|
|
|
|
private void CheckForTerritoryWait()
|
|
{
|
|
if (!questionableIPC.IsRunning())
|
|
{
|
|
return;
|
|
}
|
|
object task = questionableIPC.GetCurrentTask();
|
|
if (task == null)
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
if (!(task is JObject jObject))
|
|
{
|
|
return;
|
|
}
|
|
JToken taskNameToken = jObject["TaskName"];
|
|
if (taskNameToken == null)
|
|
{
|
|
return;
|
|
}
|
|
string taskName = taskNameToken.ToString();
|
|
if (string.IsNullOrEmpty(taskName))
|
|
{
|
|
return;
|
|
}
|
|
Match waitTerritoryMatch = new Regex("Wait\\(territory:\\s*(.+?)\\s*\\((\\d+)\\)\\)").Match(taskName);
|
|
if (!waitTerritoryMatch.Success)
|
|
{
|
|
return;
|
|
}
|
|
string territoryName = waitTerritoryMatch.Groups[1].Value.Trim();
|
|
uint territoryId = uint.Parse(waitTerritoryMatch.Groups[2].Value);
|
|
string territoryKey = $"{territoryName}_{territoryId}";
|
|
double timeSinceLastTeleport = (DateTime.Now - lastTerritoryTeleportTime).TotalSeconds;
|
|
if (lastTerritoryWaitDetected == territoryKey && timeSinceLastTeleport < 60.0)
|
|
{
|
|
return;
|
|
}
|
|
lastTerritoryWaitDetected = territoryKey;
|
|
lastTerritoryTeleportTime = DateTime.Now;
|
|
framework.RunOnFrameworkThread(delegate
|
|
{
|
|
try
|
|
{
|
|
commandManager.ProcessCommand("/li " + territoryName);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
});
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private async Task ClearPriorityQuests()
|
|
{
|
|
try
|
|
{
|
|
if (questionableIPC.IsAvailable)
|
|
{
|
|
questionableIPC.ClearQuestPriority();
|
|
await Task.Delay(500);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private void FinishPostProcess()
|
|
{
|
|
try
|
|
{
|
|
pluginInterface.GetIpcSubscriber<object>("AutoRetainer.FinishCharacterPostprocessRequest").InvokeAction();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
if (characterAdditionalTaskHandler != null && characterAdditionalTaskSubscriber != null)
|
|
{
|
|
characterAdditionalTaskSubscriber.Unsubscribe(characterAdditionalTaskHandler);
|
|
}
|
|
if (characterPostProcessHandler != null && characterPostProcessSubscriber != null)
|
|
{
|
|
characterPostProcessSubscriber.Unsubscribe(characterPostProcessHandler);
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|