Try to fix a potential issue around list not refreshing/turning in ghost items

pull/3/head v2.6
Liza 2023-10-10 23:17:24 +02:00
parent 66b248cac3
commit 74aaf1365e
Signed by: liza
GPG Key ID: 7199F8D727D55F67
6 changed files with 56 additions and 60 deletions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<Version>2.5</Version> <Version>2.6</Version>
<LangVersion>11.0</LangVersion> <LangVersion>11.0</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

View File

@ -166,13 +166,16 @@ partial class DeliverooPlugin
.ToList(); .ToList();
} }
private const int UnitListCount = 18;
private unsafe AtkUnitBase* GetAddonById(uint id) private unsafe AtkUnitBase* GetAddonById(uint id)
{ {
var unitManagers = &AtkStage.GetSingleton()->RaptureAtkUnitManager->AtkUnitManager.DepthLayerOneList; 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) if (unitBase != null && unitBase->ID == id)
{ {
return unitBase; return unitBase;
@ -203,4 +206,11 @@ partial class DeliverooPlugin
{ {
return addon->IsVisible && addon->UldManager.LoadedState == AtkLoadState.Loaded; 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;
}
} }

View File

@ -60,6 +60,7 @@ partial class DeliverooPlugin
private void OpenGcSupplyFollowUp() private void OpenGcSupplyFollowUp()
{ {
ResetTurnInErrorHandling();
CurrentStage = Stage.SelectExpertDeliveryTab; CurrentStage = Stage.SelectExpertDeliveryTab;
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Dalamud.Game.ClientState.Objects.Types; using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Memory;
using Deliveroo.GameData; using Deliveroo.GameData;
using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
@ -38,6 +39,7 @@ partial class DeliverooPlugin
if (addonGc->SelectedTab == 2) if (addonGc->SelectedTab == 2)
{ {
_pluginLog.Information("Tab already selected, probably due to haseltweaks"); _pluginLog.Information("Tab already selected, probably due to haseltweaks");
ResetTurnInErrorHandling();
CurrentStage = Stage.SelectItemToTurnIn; CurrentStage = Stage.SelectItemToTurnIn;
return; return;
} }
@ -50,11 +52,18 @@ partial class DeliverooPlugin
new() { Type = 0, Int = 0 } new() { Type = 0, Int = 0 }
}; };
addon->FireCallback(3, selectExpertDeliveryTab); addon->FireCallback(3, selectExpertDeliveryTab);
_lastTurnInListLength = int.MaxValue; ResetTurnInErrorHandling();
CurrentStage = Stage.SelectItemToTurnIn; CurrentStage = Stage.SelectItemToTurnIn;
} }
} }
private void ResetTurnInErrorHandling(int listSize = int.MaxValue)
{
_pluginLog.Verbose("Resetting error handling state");
_lastTurnInListSize = listSize;
_turnInErrors = 0;
}
private unsafe void SelectItemToTurnIn() private unsafe void SelectItemToTurnIn()
{ {
var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply); var agentInterface = AgentModule.Instance()->GetAgentByInternalId(AgentId.GrandCompanySupply);
@ -86,13 +95,35 @@ partial class DeliverooPlugin
return; 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; CurrentStage = Stage.CloseGcSupplyThenStop;
addon->FireCallbackInt(-1); addon->FireCallbackInt(-1);
return; 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; var agent = (AgentGrandCompanySupply*)agentInterface;
List<TurnInItem> items = BuildTurnInList(agent); List<TurnInItem> items = BuildTurnInList(agent);
if (items.Count == 0) if (items.Count == 0)
@ -103,19 +134,6 @@ partial class DeliverooPlugin
return; 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 // 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. // in the list, it is "only" the highest-value item to turn in.
// //
@ -158,39 +176,15 @@ partial class DeliverooPlugin
if (TryGetAddonByName<AddonGrandCompanySupplyReward>("GrandCompanySupplyReward", if (TryGetAddonByName<AddonGrandCompanySupplyReward>("GrandCompanySupplyReward",
out var addonSupplyReward) && IsAddonReady(&addonSupplyReward->AtkUnitBase)) out var addonSupplyReward) && IsAddonReady(&addonSupplyReward->AtkUnitBase))
{ {
_pluginLog.Information($"Turning in '{ReadAtkString(addonSupplyReward->AtkUnitBase.AtkValues[4])}'");
addonSupplyReward->AtkUnitBase.FireCallbackInt(0); addonSupplyReward->AtkUnitBase.FireCallbackInt(0);
_continueAt = DateTime.Now.AddSeconds(0.58); _continueAt = DateTime.Now.AddSeconds(0.58);
CurrentStage = Stage.FinalizeTurnIn1; CurrentStage = Stage.FinalizeTurnIn;
} }
} }
private unsafe void FinalizeTurnInItem1() private unsafe void FinalizeTurnInItem()
{
if (TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList",
out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase))
{
addonSupplyList->AtkUnitBase.FireCallbackInt(2);
CurrentStage = Stage.FinalizeTurnIn2;
}
}
private unsafe void FinalizeTurnInItem2()
{
if (TryGetAddonByName<AddonGrandCompanySupplyList>("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()
{ {
if (TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList", if (TryGetAddonByName<AddonGrandCompanySupplyList>("GrandCompanySupplyList",
out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase)) out var addonSupplyList) && IsAddonReady(&addonSupplyList->AtkUnitBase))

View File

@ -49,7 +49,8 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
private Stage _currentStageInternal = Stage.Stopped; private Stage _currentStageInternal = Stage.Stopped;
private DateTime _continueAt = DateTime.MinValue; private DateTime _continueAt = DateTime.MinValue;
private int _lastTurnInListLength = int.MaxValue; private int _lastTurnInListSize = int.MaxValue;
private uint _turnInErrors = 0;
private List<PurchaseItemRequest> _itemsToPurchaseNow = new(); private List<PurchaseItemRequest> _itemsToPurchaseNow = new();
public DeliverooPlugin(DalamudPluginInterface pluginInterface, IChatGui chatGui, IGameGui gameGui, public DeliverooPlugin(DalamudPluginInterface pluginInterface, IChatGui chatGui, IGameGui gameGui,
@ -240,16 +241,8 @@ public sealed partial class DeliverooPlugin : IDalamudPlugin
TurnInSelectedItem(); TurnInSelectedItem();
break; break;
case Stage.FinalizeTurnIn1: case Stage.FinalizeTurnIn:
FinalizeTurnInItem1(); FinalizeTurnInItem();
break;
case Stage.FinalizeTurnIn2:
FinalizeTurnInItem2();
break;
case Stage.FinalizeTurnIn3:
FinalizeTurnInItem3();
break; break;
case Stage.CloseGcSupply: case Stage.CloseGcSupply:

View File

@ -7,9 +7,7 @@ internal enum Stage
SelectExpertDeliveryTab, SelectExpertDeliveryTab,
SelectItemToTurnIn, SelectItemToTurnIn,
TurnInSelected, TurnInSelected,
FinalizeTurnIn1, FinalizeTurnIn,
FinalizeTurnIn2,
FinalizeTurnIn3,
CloseGcSupply, CloseGcSupply,
CloseGcSupplyThenStop, CloseGcSupplyThenStop,