695 lines
21 KiB
C#
695 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
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;
|
|
|
|
public class HelperManager : IDisposable
|
|
{
|
|
private readonly Configuration configuration;
|
|
|
|
private readonly IPluginLog log;
|
|
|
|
private readonly ICommandManager commandManager;
|
|
|
|
private readonly ICondition condition;
|
|
|
|
private readonly IClientState clientState;
|
|
|
|
private readonly IFramework framework;
|
|
|
|
private readonly PartyInviteService partyInviteService;
|
|
|
|
private readonly MultiClientIPC multiClientIPC;
|
|
|
|
private readonly CrossProcessIPC crossProcessIPC;
|
|
|
|
private readonly PartyInviteAutoAccept partyInviteAutoAccept;
|
|
|
|
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, LANHelperClient? lanHelperClient, IPartyList partyList)
|
|
{
|
|
this.configuration = configuration;
|
|
this.log = log;
|
|
this.commandManager = commandManager;
|
|
this.condition = condition;
|
|
this.clientState = clientState;
|
|
this.framework = framework;
|
|
this.partyInviteService = partyInviteService;
|
|
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;
|
|
multiClientIPC.OnHelperAvailable += OnHelperAvailable;
|
|
crossProcessIPC.OnHelperRequested += OnHelperRequested;
|
|
crossProcessIPC.OnHelperDismissed += OnHelperDismissed;
|
|
crossProcessIPC.OnHelperAvailable += OnHelperAvailable;
|
|
crossProcessIPC.OnHelperReady += OnHelperReady;
|
|
crossProcessIPC.OnHelperInParty += OnHelperInParty;
|
|
crossProcessIPC.OnHelperInDuty += OnHelperInDuty;
|
|
crossProcessIPC.OnRequestHelperAnnouncements += OnRequestHelperAnnouncements;
|
|
if (configuration.IsHighLevelHelper)
|
|
{
|
|
log.Information("[HelperManager] Will announce helper availability on next frame");
|
|
}
|
|
log.Information("[HelperManager] Initialized");
|
|
}
|
|
|
|
public void AnnounceIfHelper()
|
|
{
|
|
if (configuration.IsHighLevelHelper)
|
|
{
|
|
IPlayerCharacter localPlayer = clientState.LocalPlayer;
|
|
if (localPlayer == null)
|
|
{
|
|
log.Warning("[HelperManager] LocalPlayer is null, cannot announce helper");
|
|
return;
|
|
}
|
|
string localName = localPlayer.Name.ToString();
|
|
ushort localWorldId = (ushort)localPlayer.HomeWorld.RowId;
|
|
multiClientIPC.AnnounceHelperAvailable(localName, localWorldId);
|
|
crossProcessIPC.AnnounceHelper();
|
|
log.Information($"[HelperManager] Announced as helper: {localName}@{localWorldId} (both IPC systems)");
|
|
}
|
|
}
|
|
|
|
public void InviteHelpers()
|
|
{
|
|
if (!configuration.IsQuester)
|
|
{
|
|
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);
|
|
List<(string Name, ushort WorldId)> helpersToInvite = new List<(string, ushort)>();
|
|
if (configuration.HelperSelection == HelperSelectionMode.Auto)
|
|
{
|
|
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
|
|
{
|
|
DisbandParty();
|
|
await Task.Delay(500);
|
|
}
|
|
foreach (var (name, worldId) in helpersToInvite)
|
|
{
|
|
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)
|
|
{
|
|
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] " + 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);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
public List<(string Name, ushort WorldId)> GetAvailableHelpers()
|
|
{
|
|
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()
|
|
{
|
|
try
|
|
{
|
|
log.Information("[HelperManager] Leaving party");
|
|
framework.RunOnFrameworkThread(delegate
|
|
{
|
|
memoryHelper.SendChatMessage("/leave");
|
|
log.Information("[HelperManager] /leave command sent via UIModule");
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[HelperManager] Failed to leave party: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
public void DisbandParty()
|
|
{
|
|
try
|
|
{
|
|
log.Information("[HelperManager] Disbanding party");
|
|
framework.RunOnFrameworkThread(delegate
|
|
{
|
|
memoryHelper.SendChatMessage("/leave");
|
|
log.Information("[HelperManager] /leave command sent via UIModule");
|
|
});
|
|
multiClientIPC.DismissHelper();
|
|
crossProcessIPC.DismissHelper();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[HelperManager] Failed to disband party: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private void OnConditionChanged(ConditionFlag flag, bool value)
|
|
{
|
|
if (flag == ConditionFlag.BoundByDuty)
|
|
{
|
|
if (value && !isInDuty)
|
|
{
|
|
isInDuty = true;
|
|
OnDutyEnter();
|
|
}
|
|
else if (!value && isInDuty)
|
|
{
|
|
isInDuty = false;
|
|
OnDutyLeave();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnDutyEnter()
|
|
{
|
|
log.Debug("[HelperManager] Entered duty");
|
|
if (!configuration.IsHighLevelHelper)
|
|
{
|
|
return;
|
|
}
|
|
configuration.CurrentHelperStatus = HelperStatus.InDungeon;
|
|
configuration.Save();
|
|
log.Information("[HelperManager] Helper status: InDungeon");
|
|
IPlayerCharacter localPlayer = clientState.LocalPlayer;
|
|
if (localPlayer != null)
|
|
{
|
|
string helperName = localPlayer.Name.ToString();
|
|
ushort helperWorld = (ushort)localPlayer.HomeWorld.RowId;
|
|
crossProcessIPC.BroadcastHelperStatus(helperName, helperWorld, "InDungeon");
|
|
}
|
|
log.Information("[HelperManager] Starting AutoDuty (High-Level Helper)");
|
|
Task.Run(async delegate
|
|
{
|
|
log.Information("[HelperManager] Waiting 5s before starting AutoDuty...");
|
|
await Task.Delay(5000);
|
|
framework.RunOnFrameworkThread(delegate
|
|
{
|
|
try
|
|
{
|
|
commandManager.ProcessCommand("/ad start");
|
|
log.Information("[HelperManager] AutoDuty started");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[HelperManager] Failed to start AutoDuty: " + ex.Message);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
private unsafe void OnDutyLeave()
|
|
{
|
|
log.Information("[HelperManager] Left duty");
|
|
if (configuration.IsHighLevelHelper)
|
|
{
|
|
if (configuration.CurrentHelperStatus == HelperStatus.InDungeon)
|
|
{
|
|
configuration.CurrentHelperStatus = HelperStatus.Available;
|
|
configuration.Save();
|
|
log.Information("[HelperManager] Helper status: Available");
|
|
IPlayerCharacter localPlayer = clientState.LocalPlayer;
|
|
if (localPlayer != null)
|
|
{
|
|
string helperName = localPlayer.Name.ToString();
|
|
ushort helperWorld = (ushort)localPlayer.HomeWorld.RowId;
|
|
crossProcessIPC.BroadcastHelperStatus(helperName, helperWorld, "Available");
|
|
}
|
|
}
|
|
log.Information("[HelperManager] Stopping AutoDuty (High-Level Helper)");
|
|
framework.RunOnFrameworkThread(delegate
|
|
{
|
|
try
|
|
{
|
|
commandManager.ProcessCommand("/ad stop");
|
|
log.Information("[HelperManager] AutoDuty stopped");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error("[HelperManager] Failed to stop AutoDuty: " + ex.Message);
|
|
}
|
|
});
|
|
log.Information("[HelperManager] Leaving party after duty (High-Level Helper)");
|
|
Task.Run(async delegate
|
|
{
|
|
log.Information("[HelperManager] Waiting 4 seconds for duty to fully complete...");
|
|
await Task.Delay(4000);
|
|
for (int attempt = 1; attempt <= 3; attempt++)
|
|
{
|
|
bool inParty = false;
|
|
GroupManager* groupManager = GroupManager.Instance();
|
|
if (groupManager != null)
|
|
{
|
|
GroupManager.Group* group = groupManager->GetGroup();
|
|
if (group != null && group->MemberCount > 1)
|
|
{
|
|
inParty = true;
|
|
}
|
|
}
|
|
if (!inParty)
|
|
{
|
|
log.Information("[HelperManager] Successfully left party or already solo");
|
|
break;
|
|
}
|
|
log.Information($"[HelperManager] Attempt {attempt}/3: Still in party - sending /leave command");
|
|
LeaveParty();
|
|
if (attempt < 3)
|
|
{
|
|
await Task.Delay(2000);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (configuration.IsQuester)
|
|
{
|
|
log.Information("[HelperManager] Disbanding party after duty (Quester)");
|
|
DisbandParty();
|
|
}
|
|
}
|
|
|
|
private unsafe void OnHelperRequested(string characterName, ushort worldId)
|
|
{
|
|
if (!configuration.IsHighLevelHelper)
|
|
{
|
|
log.Debug("[HelperManager] Not a High-Level Helper, ignoring request");
|
|
return;
|
|
}
|
|
IPlayerCharacter localPlayer = clientState.LocalPlayer;
|
|
if (localPlayer == null)
|
|
{
|
|
log.Warning("[HelperManager] Local player is null!");
|
|
return;
|
|
}
|
|
string localName = localPlayer.Name.ToString();
|
|
ushort localWorldId = (ushort)localPlayer.HomeWorld.RowId;
|
|
if (!(localName == characterName) || localWorldId != worldId)
|
|
{
|
|
return;
|
|
}
|
|
log.Information("[HelperManager] Helper request is for me! Checking status...");
|
|
Task.Run(async delegate
|
|
{
|
|
bool needsToLeaveParty = false;
|
|
bool isInDuty = false;
|
|
GroupManager* groupManager = GroupManager.Instance();
|
|
if (groupManager != null)
|
|
{
|
|
GroupManager.Group* group = groupManager->GetGroup();
|
|
if (group != null && group->MemberCount > 0)
|
|
{
|
|
bool requesterInParty = false;
|
|
if (partyList != null)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!isInDuty)
|
|
{
|
|
if (needsToLeaveParty)
|
|
{
|
|
LeaveParty();
|
|
await Task.Delay(1000);
|
|
}
|
|
log.Information("[HelperManager] Ready to accept invite!");
|
|
partyInviteAutoAccept.EnableAutoAccept();
|
|
crossProcessIPC.NotifyHelperReady(localName, localWorldId);
|
|
}
|
|
});
|
|
}
|
|
|
|
private void OnHelperDismissed()
|
|
{
|
|
if (configuration.IsHighLevelHelper)
|
|
{
|
|
log.Information("[HelperManager] Received dismiss signal, leaving party...");
|
|
DisbandParty();
|
|
}
|
|
}
|
|
|
|
private void OnHelperAvailable(string characterName, ushort worldId)
|
|
{
|
|
if (configuration.IsQuester && !availableHelpers.Any<(string, ushort)>(((string Name, ushort WorldId) h) => h.Name == characterName && h.WorldId == worldId))
|
|
{
|
|
availableHelpers.Add((characterName, worldId));
|
|
log.Information($"[HelperManager] Helper discovered: {characterName}@{worldId} (Total: {availableHelpers.Count})");
|
|
}
|
|
}
|
|
|
|
private void OnHelperReady(string characterName, ushort worldId)
|
|
{
|
|
if (configuration.IsQuester)
|
|
{
|
|
log.Information($"[HelperManager] Helper {characterName}@{worldId} is ready!");
|
|
helperReadyStatus[(characterName, worldId)] = true;
|
|
}
|
|
}
|
|
|
|
private void OnHelperInParty(string characterName, ushort worldId)
|
|
{
|
|
if (configuration.IsQuester)
|
|
{
|
|
log.Information($"[HelperManager] Helper {characterName}@{worldId} is in a party, waiting for them to leave...");
|
|
}
|
|
}
|
|
|
|
private void OnHelperInDuty(string characterName, ushort worldId)
|
|
{
|
|
if (configuration.IsQuester)
|
|
{
|
|
log.Warning($"[HelperManager] Helper {characterName}@{worldId} is in a duty! Cannot invite until they leave.");
|
|
}
|
|
}
|
|
|
|
private void OnRequestHelperAnnouncements()
|
|
{
|
|
if (configuration.IsHighLevelHelper)
|
|
{
|
|
log.Information("[HelperManager] Received request for helper announcements, announcing...");
|
|
AnnounceIfHelper();
|
|
}
|
|
}
|
|
|
|
public void RequestHelperAnnouncements()
|
|
{
|
|
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;
|
|
multiClientIPC.OnHelperRequested -= OnHelperRequested;
|
|
multiClientIPC.OnHelperDismissed -= OnHelperDismissed;
|
|
multiClientIPC.OnHelperAvailable -= OnHelperAvailable;
|
|
crossProcessIPC.OnHelperRequested -= OnHelperRequested;
|
|
crossProcessIPC.OnHelperDismissed -= OnHelperDismissed;
|
|
crossProcessIPC.OnHelperAvailable -= OnHelperAvailable;
|
|
crossProcessIPC.OnHelperReady -= OnHelperReady;
|
|
crossProcessIPC.OnHelperInParty -= OnHelperInParty;
|
|
crossProcessIPC.OnHelperInDuty -= OnHelperInDuty;
|
|
crossProcessIPC.OnRequestHelperAnnouncements -= OnRequestHelperAnnouncements;
|
|
}
|
|
}
|