qstcompanion v1.0.6
This commit is contained in:
parent
5e1e1decc5
commit
ada27cf05b
30 changed files with 3403 additions and 426 deletions
|
|
@ -4,8 +4,12 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Game.ClientState.Party;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Group;
|
||||
using Lumina.Excel;
|
||||
using Lumina.Excel.Sheets;
|
||||
using QuestionableCompanion.Models;
|
||||
|
||||
namespace QuestionableCompanion.Services;
|
||||
|
||||
|
|
@ -33,13 +37,17 @@ public class HelperManager : IDisposable
|
|||
|
||||
private readonly MemoryHelper memoryHelper;
|
||||
|
||||
private readonly LANHelperClient? lanHelperClient;
|
||||
|
||||
private readonly IPartyList partyList;
|
||||
|
||||
private bool isInDuty;
|
||||
|
||||
private List<(string Name, ushort WorldId)> availableHelpers = new List<(string, ushort)>();
|
||||
|
||||
private Dictionary<(string, ushort), bool> helperReadyStatus = new Dictionary<(string, ushort), bool>();
|
||||
|
||||
public HelperManager(Configuration configuration, IPluginLog log, ICommandManager commandManager, ICondition condition, IClientState clientState, IFramework framework, PartyInviteService partyInviteService, MultiClientIPC multiClientIPC, CrossProcessIPC crossProcessIPC, PartyInviteAutoAccept partyInviteAutoAccept, MemoryHelper memoryHelper)
|
||||
public HelperManager(Configuration configuration, IPluginLog log, ICommandManager commandManager, ICondition condition, IClientState clientState, IFramework framework, PartyInviteService partyInviteService, MultiClientIPC multiClientIPC, CrossProcessIPC crossProcessIPC, PartyInviteAutoAccept partyInviteAutoAccept, MemoryHelper memoryHelper, LANHelperClient? lanHelperClient, IPartyList partyList)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.log = log;
|
||||
|
|
@ -51,7 +59,9 @@ public class HelperManager : IDisposable
|
|||
this.multiClientIPC = multiClientIPC;
|
||||
this.crossProcessIPC = crossProcessIPC;
|
||||
this.memoryHelper = memoryHelper;
|
||||
this.lanHelperClient = lanHelperClient;
|
||||
this.partyInviteAutoAccept = partyInviteAutoAccept;
|
||||
this.partyList = partyList;
|
||||
condition.ConditionChange += OnConditionChanged;
|
||||
multiClientIPC.OnHelperRequested += OnHelperRequested;
|
||||
multiClientIPC.OnHelperDismissed += OnHelperDismissed;
|
||||
|
|
@ -95,56 +105,236 @@ public class HelperManager : IDisposable
|
|||
log.Debug("[HelperManager] Not a Quester, skipping helper invites");
|
||||
return;
|
||||
}
|
||||
if (configuration.HelperSelection == HelperSelectionMode.ManualInput)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configuration.ManualHelperName))
|
||||
{
|
||||
log.Warning("[HelperManager] Manual Input mode selected but no helper name configured!");
|
||||
return;
|
||||
}
|
||||
Task.Run(async delegate
|
||||
{
|
||||
log.Information("[HelperManager] Manual Input mode: Inviting " + configuration.ManualHelperName);
|
||||
string[] parts = configuration.ManualHelperName.Split('@');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
log.Error("[HelperManager] Invalid manual helper format: " + configuration.ManualHelperName + " (expected: CharacterName@WorldName)");
|
||||
}
|
||||
else
|
||||
{
|
||||
string helperName = parts[0].Trim();
|
||||
string worldName = parts[1].Trim();
|
||||
ushort worldId = 0;
|
||||
ExcelSheet<World> worldSheet = Plugin.DataManager.GetExcelSheet<World>();
|
||||
if (worldSheet != null)
|
||||
{
|
||||
foreach (World world in worldSheet)
|
||||
{
|
||||
if (world.Name.ExtractText().Equals(worldName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
worldId = (ushort)world.RowId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (worldId == 0)
|
||||
{
|
||||
log.Error("[HelperManager] Could not find world ID for: " + worldName);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Information($"[HelperManager] Resolved helper: {helperName}@{worldId} ({worldName})");
|
||||
bool alreadyInParty = false;
|
||||
if (partyList != null)
|
||||
{
|
||||
foreach (IPartyMember member in partyList)
|
||||
{
|
||||
if (member.Name.ToString() == helperName && member.World.RowId == worldId)
|
||||
{
|
||||
alreadyInParty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alreadyInParty)
|
||||
{
|
||||
log.Information("[HelperManager] helper " + helperName + " is ALREADY in party! Skipping disband/invite.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DisbandParty();
|
||||
await Task.Delay(500);
|
||||
log.Information("[HelperManager] Sending direct invite to " + helperName + " (Manual Input - no IPC wait)");
|
||||
if (partyInviteService.InviteToParty(helperName, worldId))
|
||||
{
|
||||
log.Information("[HelperManager] Successfully invited " + helperName);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Error("[HelperManager] Failed to invite " + helperName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
log.Information("[HelperManager] Requesting helper announcements...");
|
||||
RequestHelperAnnouncements();
|
||||
Task.Run(async delegate
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
if (availableHelpers.Count == 0)
|
||||
List<(string Name, ushort WorldId)> helpersToInvite = new List<(string, ushort)>();
|
||||
if (configuration.HelperSelection == HelperSelectionMode.Auto)
|
||||
{
|
||||
log.Warning("[HelperManager] No helpers available via IPC!");
|
||||
log.Warning("[HelperManager] Make sure helper clients are running with 'I'm a High-Level Helper' enabled");
|
||||
if (availableHelpers.Count == 0)
|
||||
{
|
||||
log.Warning("[HelperManager] No helpers available via IPC!");
|
||||
if (lanHelperClient != null)
|
||||
{
|
||||
log.Information("[HelperManager] Checking for LAN helpers...");
|
||||
LANHelperInfo lanHelper = lanHelperClient.GetFirstAvailableHelper();
|
||||
if (lanHelper != null)
|
||||
{
|
||||
log.Information("[HelperManager] Found LAN helper: " + lanHelper.Name + " at " + lanHelper.IPAddress);
|
||||
await InviteLANHelper(lanHelper.IPAddress, lanHelper.Name, lanHelper.WorldId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
log.Warning("[HelperManager] Make sure helper clients are running with 'I'm a High-Level Helper' enabled");
|
||||
return;
|
||||
}
|
||||
helpersToInvite.AddRange(availableHelpers);
|
||||
log.Information($"[HelperManager] Auto mode: Inviting {helpersToInvite.Count} AUTO-DISCOVERED helper(s)...");
|
||||
}
|
||||
else if (configuration.HelperSelection == HelperSelectionMode.Dropdown)
|
||||
{
|
||||
if (string.IsNullOrEmpty(configuration.PreferredHelper))
|
||||
{
|
||||
log.Warning("[HelperManager] Dropdown mode selected but no helper chosen!");
|
||||
return;
|
||||
}
|
||||
string[] parts = configuration.PreferredHelper.Split('@');
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
log.Error("[HelperManager] Invalid preferred helper format: " + configuration.PreferredHelper);
|
||||
return;
|
||||
}
|
||||
string helperName = parts[0].Trim();
|
||||
string worldName = parts[1].Trim();
|
||||
(string, ushort) matchingHelper = availableHelpers.FirstOrDefault<(string, ushort)>(delegate((string Name, ushort WorldId) h)
|
||||
{
|
||||
ExcelSheet<World> excelSheet = Plugin.DataManager.GetExcelSheet<World>();
|
||||
string text2 = "Unknown";
|
||||
if (excelSheet != null)
|
||||
{
|
||||
foreach (World current in excelSheet)
|
||||
{
|
||||
if (current.RowId == h.WorldId)
|
||||
{
|
||||
text2 = current.Name.ExtractText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return h.Name == helperName && text2 == worldName;
|
||||
});
|
||||
var (text, num) = matchingHelper;
|
||||
if (text == null && num == 0)
|
||||
{
|
||||
log.Warning("[HelperManager] Preferred helper " + configuration.PreferredHelper + " not found in discovered helpers!");
|
||||
return;
|
||||
}
|
||||
helpersToInvite.Add(matchingHelper);
|
||||
log.Information("[HelperManager] Dropdown mode: Inviting selected helper " + configuration.PreferredHelper);
|
||||
}
|
||||
bool allHelpersPresent = false;
|
||||
if (partyList != null && partyList.Length > 0 && helpersToInvite.Count > 0)
|
||||
{
|
||||
int presentCount = 0;
|
||||
foreach (var (hName, hWorld) in helpersToInvite)
|
||||
{
|
||||
foreach (IPartyMember member in partyList)
|
||||
{
|
||||
if (member.Name.ToString() == hName && member.World.RowId == hWorld)
|
||||
{
|
||||
presentCount++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (presentCount >= helpersToInvite.Count)
|
||||
{
|
||||
allHelpersPresent = true;
|
||||
}
|
||||
}
|
||||
if (allHelpersPresent)
|
||||
{
|
||||
log.Information("[HelperManager] All desired helpers are ALREADY in party! Skipping disband.");
|
||||
}
|
||||
else if (partyList != null && partyList.Length > 1)
|
||||
{
|
||||
bool anyHelperPresent = false;
|
||||
foreach (var (hName2, hWorld2) in helpersToInvite)
|
||||
{
|
||||
foreach (IPartyMember member2 in partyList)
|
||||
{
|
||||
if (member2.Name.ToString() == hName2 && member2.World.RowId == hWorld2)
|
||||
{
|
||||
anyHelperPresent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (anyHelperPresent)
|
||||
{
|
||||
log.Information("[HelperManager] Some helpers already in party - NOT disbanding, simply inviting remaining.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DisbandParty();
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Information($"[HelperManager] Inviting {availableHelpers.Count} AUTO-DISCOVERED helper(s)...");
|
||||
DisbandParty();
|
||||
await Task.Delay(500);
|
||||
foreach (var (name, worldId) in availableHelpers)
|
||||
}
|
||||
foreach (var (name, worldId) in helpersToInvite)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || worldId == 0)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name) || worldId == 0)
|
||||
log.Warning($"[HelperManager] Invalid helper: {name}@{worldId}");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Information($"[HelperManager] Requesting helper: {name}@{worldId}");
|
||||
helperReadyStatus[(name, worldId)] = false;
|
||||
multiClientIPC.RequestHelper(name, worldId);
|
||||
crossProcessIPC.RequestHelper(name, worldId);
|
||||
log.Information("[HelperManager] Waiting for " + name + " to be ready...");
|
||||
DateTime timeout = DateTime.Now.AddSeconds(10.0);
|
||||
while (!helperReadyStatus.GetValueOrDefault((name, worldId), defaultValue: false) && DateTime.Now < timeout)
|
||||
{
|
||||
log.Warning($"[HelperManager] Invalid helper: {name}@{worldId}");
|
||||
await Task.Delay(100);
|
||||
}
|
||||
if (!helperReadyStatus.GetValueOrDefault((name, worldId), defaultValue: false))
|
||||
{
|
||||
log.Warning("[HelperManager] Timeout waiting for " + name + " to be ready!");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Information($"[HelperManager] Requesting helper: {name}@{worldId}");
|
||||
helperReadyStatus[(name, worldId)] = false;
|
||||
multiClientIPC.RequestHelper(name, worldId);
|
||||
crossProcessIPC.RequestHelper(name, worldId);
|
||||
log.Information("[HelperManager] Waiting for " + name + " to be ready...");
|
||||
DateTime timeout = DateTime.Now.AddSeconds(10.0);
|
||||
while (!helperReadyStatus.GetValueOrDefault((name, worldId), defaultValue: false) && DateTime.Now < timeout)
|
||||
log.Information("[HelperManager] " + name + " is ready! Sending invite...");
|
||||
if (partyInviteService.InviteToParty(name, worldId))
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
if (!helperReadyStatus.GetValueOrDefault((name, worldId), defaultValue: false))
|
||||
{
|
||||
log.Warning("[HelperManager] Timeout waiting for " + name + " to be ready!");
|
||||
log.Information("[HelperManager] Successfully invited " + name);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Information("[HelperManager] " + name + " is ready! Sending invite...");
|
||||
if (partyInviteService.InviteToParty(name, worldId))
|
||||
{
|
||||
log.Information("[HelperManager] Successfully invited " + name);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.Error("[HelperManager] Failed to invite " + name);
|
||||
}
|
||||
await Task.Delay(500);
|
||||
log.Error("[HelperManager] Failed to invite " + name);
|
||||
}
|
||||
await Task.Delay(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +343,18 @@ public class HelperManager : IDisposable
|
|||
|
||||
public List<(string Name, ushort WorldId)> GetAvailableHelpers()
|
||||
{
|
||||
return new List<(string, ushort)>(availableHelpers);
|
||||
List<(string, ushort)> allHelpers = new List<(string, ushort)>(availableHelpers);
|
||||
if (lanHelperClient != null)
|
||||
{
|
||||
foreach (LANHelperInfo lanHelper in lanHelperClient.DiscoveredHelpers)
|
||||
{
|
||||
if (!allHelpers.Any<(string, ushort)>(((string Name, ushort WorldId) h) => h.Name == lanHelper.Name && h.WorldId == lanHelper.WorldId))
|
||||
{
|
||||
allHelpers.Add((lanHelper.Name, lanHelper.WorldId));
|
||||
}
|
||||
}
|
||||
}
|
||||
return allHelpers;
|
||||
}
|
||||
|
||||
private void LeaveParty()
|
||||
|
|
@ -211,7 +412,7 @@ public class HelperManager : IDisposable
|
|||
|
||||
private void OnDutyEnter()
|
||||
{
|
||||
log.Information("[HelperManager] Entered duty");
|
||||
log.Debug("[HelperManager] Entered duty");
|
||||
if (!configuration.IsHighLevelHelper)
|
||||
{
|
||||
return;
|
||||
|
|
@ -345,14 +546,34 @@ public class HelperManager : IDisposable
|
|||
GroupManager.Group* group = groupManager->GetGroup();
|
||||
if (group != null && group->MemberCount > 0)
|
||||
{
|
||||
needsToLeaveParty = true;
|
||||
log.Information("[HelperManager] Currently in party, notifying quester...");
|
||||
crossProcessIPC.NotifyHelperInParty(localName, localWorldId);
|
||||
if (condition[ConditionFlag.BoundByDuty])
|
||||
bool requesterInParty = false;
|
||||
if (partyList != null)
|
||||
{
|
||||
isInDuty = true;
|
||||
log.Information("[HelperManager] Currently in duty, notifying quester...");
|
||||
crossProcessIPC.NotifyHelperInDuty(localName, localWorldId);
|
||||
foreach (IPartyMember member in partyList)
|
||||
{
|
||||
if (member.Name.ToString() == characterName && member.World.RowId == worldId)
|
||||
{
|
||||
requesterInParty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (requesterInParty)
|
||||
{
|
||||
log.Information($"[HelperManager] Request from {characterName}@{worldId} who is ALREADY in my party! Ignoring leave request.");
|
||||
needsToLeaveParty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
needsToLeaveParty = true;
|
||||
log.Information("[HelperManager] Currently in party (but not with requester), notifying quester...");
|
||||
crossProcessIPC.NotifyHelperInParty(localName, localWorldId);
|
||||
if (condition[ConditionFlag.BoundByDuty])
|
||||
{
|
||||
isInDuty = true;
|
||||
log.Information("[HelperManager] Currently in duty, notifying quester...");
|
||||
crossProcessIPC.NotifyHelperInDuty(localName, localWorldId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -427,6 +648,36 @@ public class HelperManager : IDisposable
|
|||
crossProcessIPC.RequestHelperAnnouncements();
|
||||
}
|
||||
|
||||
private async Task InviteLANHelper(string ipAddress, string helperName, ushort worldId)
|
||||
{
|
||||
if (lanHelperClient == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
log.Information("[HelperManager] ========================================");
|
||||
log.Information("[HelperManager] === INVITING LAN HELPER ===");
|
||||
log.Information("[HelperManager] Helper: " + helperName);
|
||||
log.Information("[HelperManager] IP: " + ipAddress);
|
||||
log.Information("[HelperManager] ========================================");
|
||||
DisbandParty();
|
||||
await Task.Delay(500);
|
||||
log.Information("[HelperManager] Sending helper request to " + ipAddress + "...");
|
||||
if (!(await lanHelperClient.RequestHelperAsync(ipAddress, "LAN Dungeon")))
|
||||
{
|
||||
log.Error("[HelperManager] Failed to send helper request to " + ipAddress);
|
||||
return;
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
log.Information("[HelperManager] Sending party invite to " + helperName + "...");
|
||||
if (!partyInviteService.InviteToParty(helperName, worldId))
|
||||
{
|
||||
log.Error("[HelperManager] Failed to invite " + helperName + " to party");
|
||||
return;
|
||||
}
|
||||
await lanHelperClient.NotifyInviteSentAsync(ipAddress, helperName);
|
||||
log.Information("[HelperManager] ✓ LAN helper invite complete");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
condition.ConditionChange -= OnConditionChanged;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue