diff --git a/Deliveroo/Deliveroo.csproj b/Deliveroo/Deliveroo.csproj index 61e6956..33e0ec2 100644 --- a/Deliveroo/Deliveroo.csproj +++ b/Deliveroo/Deliveroo.csproj @@ -1,7 +1,7 @@ net7.0-windows - 2.5 + 2.6 11.0 enable true diff --git a/Deliveroo/DeliverooPlugin.GameFunctions.cs b/Deliveroo/DeliverooPlugin.GameFunctions.cs index 16ab066..75858b5 100644 --- a/Deliveroo/DeliverooPlugin.GameFunctions.cs +++ b/Deliveroo/DeliverooPlugin.GameFunctions.cs @@ -166,13 +166,16 @@ partial class DeliverooPlugin .ToList(); } + private const int UnitListCount = 18; private unsafe AtkUnitBase* GetAddonById(uint id) { var unitManagers = &AtkStage.GetSingleton()->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList; - for (var i = 0; i < 18; i++) + for (var i = 0; i < UnitListCount; i++) { - foreach (AtkUnitBase* unitBase in unitManagers[i].EntriesSpan) + var unitManager = &unitManagers[i]; + foreach (var j in Enumerable.Range(0, Math.Min(unitManager->Count, unitManager->EntriesSpan.Length))) { + var unitBase = unitManager->EntriesSpan[j].Value; if (unitBase != null && unitBase->ID == id) { return unitBase; @@ -203,4 +206,11 @@ partial class DeliverooPlugin { return addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded; } + + private unsafe string? ReadAtkString(AtkValue atkValue) + { + if (atkValue.String != null) + return MemoryHelper.ReadSeStringNullTerminated(new nint(atkValue.String)).ToString(); + return null; + } } diff --git a/Deliveroo/DeliverooPlugin.SelectString.cs b/Deliveroo/DeliverooPlugin.SelectString.cs index 9d9d7fe..2f84e37 100644 --- a/Deliveroo/DeliverooPlugin.SelectString.cs +++ b/Deliveroo/DeliverooPlugin.SelectString.cs @@ -60,6 +60,7 @@ partial class DeliverooPlugin private void OpenGcSupplyFollowUp() { + ResetTurnInErrorHandling(); CurrentStage = Stage.SelectExpertDeliveryTab; } diff --git a/Deliveroo/DeliverooPlugin.Supply.cs b/Deliveroo/DeliverooPlugin.Supply.cs index ff57d44..b9534b2 100644 --- a/Deliveroo/DeliverooPlugin.Supply.cs +++ b/Deliveroo/DeliverooPlugin.Supply.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Dalamud.Game.ClientState.Objects.Types; +using Dalamud.Memory; using Deliveroo.GameData; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI.Agent; @@ -38,6 +39,7 @@ partial class DeliverooPlugin if (addonGc->SelectedTab == 2) { _pluginLog.Information("Tab already selected, probably due to haseltweaks"); + ResetTurnInErrorHandling(); CurrentStage = Stage.SelectItemToTurnIn; return; } @@ -50,11 +52,18 @@ partial class DeliverooPlugin new() { Type = 0, Int = 0 } }; addon->FireCallback(3, selectExpertDeliveryTab); - _lastTurnInListLength = int.MaxValue; + ResetTurnInErrorHandling(); CurrentStage = Stage.SelectItemToTurnIn; } } + private void ResetTurnInErrorHandling(int listSize = int.MaxValue) + { + _pluginLog.Verbose("Resetting error handling state"); + _lastTurnInListSize = listSize; + _turnInErrors = 0; + } + private unsafe void SelectItemToTurnIn() { var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply); @@ -86,13 +95,35 @@ partial class DeliverooPlugin return; } - if (addonGc->ListEmptyTextNode->AtkResNode.IsVisible) + int currentListSize = addonGc->ExpertDeliveryList->ListLength; + if (addonGc->ListEmptyTextNode->AtkResNode.IsVisible || currentListSize == 0) { + _pluginLog.Information($"No items to turn in {addonGc->ListEmptyTextNode->AtkResNode.IsVisible}, {currentListSize})"); CurrentStage = Stage.CloseGcSupplyThenStop; addon->FireCallbackInt(-1); return; } + // Fallback: Two successive calls to SelectItemToTurnIn should *not* have lists of the same length, or + // something is wrong. + if (_turnInErrors > 10) + { + _turnInWindow.Error = "Unable to refresh item list"; + return; + } + + if (currentListSize >= _lastTurnInListSize) + { + _turnInErrors++; + _pluginLog.Information($"Trying to refresh expert delivery list manually ({_turnInErrors}, old list size = {_lastTurnInListSize}, new list size = {currentListSize})..."); + addon->FireCallbackInt(2); + + _continueAt = DateTime.Now.AddSeconds(0.1); + return; + } + + ResetTurnInErrorHandling(currentListSize); + var agent = (AgentGrandCompanySupply*)agentInterface; List items = BuildTurnInList(agent); if (items.Count == 0) @@ -103,19 +134,6 @@ partial class DeliverooPlugin return; } - // Fallback: Two successive calls to SelectItemToTurnIn should *not* have lists of the same length, or - // something is wrong. - if (items.Count >= _lastTurnInListLength) - { - _pluginLog.Warning("Closing GC supply window, possible invalid loop detected"); - - CurrentStage = Stage.CloseGcSupply; - addon->FireCallbackInt(-1); - return; - } - - _lastTurnInListLength = items.Count; - // TODO The way the items are handled above, we don't actually know if items[0] is the first visible item // in the list, it is "only" the highest-value item to turn in. // @@ -158,39 +176,15 @@ partial class DeliverooPlugin if (TryGetAddonByName("GrandCompanySupplyReward", out var addonSupplyReward) && IsAddonReady(&addonSupplyReward->AtkUnitBase)) { + _pluginLog.Information($"Turning in '{ReadAtkString(addonSupplyReward->AtkUnitBase.AtkValues[4])}'"); + addonSupplyReward->AtkUnitBase.FireCallbackInt(0); _continueAt = DateTime.Now.AddSeconds(0.58); - CurrentStage = Stage.FinalizeTurnIn1; + CurrentStage = Stage.FinalizeTurnIn; } } - private unsafe void FinalizeTurnInItem1() - { - if (TryGetAddonByName("GrandCompanySupplyList", - out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase)) - { - addonSupplyList->AtkUnitBase.FireCallbackInt(2); - CurrentStage = Stage.FinalizeTurnIn2; - } - } - - private unsafe void FinalizeTurnInItem2() - { - if (TryGetAddonByName("GrandCompanySupplyList", - out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase)) - { - var updateUnknown = stackalloc AtkValue[] - { - new() { Type = ValueType.Int, Int = 4 }, - new() { Type = ValueType.Int, Int = 0 }, - new() { Type = 0, Int = 0 } - }; - addonSupplyList->AtkUnitBase.FireCallback(3, updateUnknown); - CurrentStage = Stage.FinalizeTurnIn3; - } - } - - private unsafe void FinalizeTurnInItem3() + private unsafe void FinalizeTurnInItem() { if (TryGetAddonByName("GrandCompanySupplyList", out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase)) diff --git a/Deliveroo/DeliverooPlugin.cs b/Deliveroo/DeliverooPlugin.cs index 979109f..48b3e9e 100644 --- a/Deliveroo/DeliverooPlugin.cs +++ b/Deliveroo/DeliverooPlugin.cs @@ -49,7 +49,8 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin private Stage _currentStageInternal = Stage.Stopped; private DateTime _continueAt = DateTime.MinValue; - private int _lastTurnInListLength = int.MaxValue; + private int _lastTurnInListSize = int.MaxValue; + private uint _turnInErrors = 0; private List _itemsToPurchaseNow = new(); public DeliverooPlugin(DalamudPluginInterface pluginInterface, IChatGui chatGui, IGameGui gameGui, @@ -240,16 +241,8 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin TurnInSelectedItem(); break; - case Stage.FinalizeTurnIn1: - FinalizeTurnInItem1(); - break; - - case Stage.FinalizeTurnIn2: - FinalizeTurnInItem2(); - break; - - case Stage.FinalizeTurnIn3: - FinalizeTurnInItem3(); + case Stage.FinalizeTurnIn: + FinalizeTurnInItem(); break; case Stage.CloseGcSupply: diff --git a/Deliveroo/Stage.cs b/Deliveroo/Stage.cs index 79d63ac..5fbe112 100644 --- a/Deliveroo/Stage.cs +++ b/Deliveroo/Stage.cs @@ -7,9 +7,7 @@ internal enum Stage SelectExpertDeliveryTab, SelectItemToTurnIn, TurnInSelected, - FinalizeTurnIn1, - FinalizeTurnIn2, - FinalizeTurnIn3, + FinalizeTurnIn, CloseGcSupply, CloseGcSupplyThenStop,