diff --git a/Dalamud/Game/AddonLifecycle/AddonEvent.cs b/Dalamud/Game/AddonLifecycle/AddonEvent.cs index 30121d162..faef30c88 100644 --- a/Dalamud/Game/AddonLifecycle/AddonEvent.cs +++ b/Dalamud/Game/AddonLifecycle/AddonEvent.cs @@ -49,4 +49,14 @@ public enum AddonEvent /// Event that is fired after an addon finishes a requested update. /// PostRequestedUpdate, + + /// + /// Event that is fired before an addon begins a refresh. + /// + PreRefresh, + + /// + /// Event that is fired after an addon has finished a refresh. + /// + PostRefresh, } diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs index 9bc792f46..d6da18dd5 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs +++ b/Dalamud/Game/AddonLifecycle/AddonLifecycle.cs @@ -30,6 +30,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType private readonly Hook onAddonFinalizeHook; private readonly CallHook onAddonDrawHook; private readonly CallHook onAddonUpdateHook; + private readonly Hook onAddonRefreshHook; // private readonly CallHook onAddonRequestedUpdateHook; // See Note in Ctor private readonly ConcurrentBag newEventListeners = new(); @@ -48,6 +49,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType this.onAddonFinalizeHook = Hook.FromAddress(this.address.AddonFinalize, this.OnAddonFinalize); this.onAddonDrawHook = new CallHook(this.address.AddonDraw, this.OnAddonDraw); this.onAddonUpdateHook = new CallHook(this.address.AddonUpdate, this.OnAddonUpdate); + this.onAddonRefreshHook = Hook.FromAddress(this.address.AddonOnRefresh, this.OnAddonRefresh); // todo: reenable this. WARNING: This hook overwrites a system that SimpleTweaks uses, causing SimpleTweaks to report exceptions. // this.onAddonRequestedUpdateHook = new CallHook(this.address.AddonOnRequestedUpdate, this.OnRequestedUpdate); @@ -61,7 +63,9 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType private delegate void AddonUpdateDelegate(AtkUnitBase* addon, float delta); - private delegate void AddonOnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData); + private delegate void AddonOnRequestedUpdateDelegate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData); + + private delegate void AddonOnRefreshDelegate(AtkUnitManager* unitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values); /// public void Dispose() @@ -72,6 +76,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType this.onAddonFinalizeHook.Dispose(); this.onAddonDrawHook.Dispose(); this.onAddonUpdateHook.Dispose(); + this.onAddonRefreshHook.Dispose(); // this.onAddonRequestedUpdateHook.Dispose(); // See Note in Ctor } @@ -120,6 +125,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType this.onAddonFinalizeHook.Enable(); this.onAddonDrawHook.Enable(); this.onAddonUpdateHook.Enable(); + this.onAddonRefreshHook.Enable(); // this.onAddonRequestedUpdateHook.Enable(); // See Note in Ctor } @@ -226,6 +232,29 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType } } + private void OnAddonRefresh(AtkUnitManager* atkUnitManager, AtkUnitBase* addon, uint valueCount, AtkValue* values) + { + try + { + this.InvokeListeners(AddonEvent.PreRefresh, new IAddonLifecycle.AddonArgs { Addon = (nint)addon }); + } + catch (Exception e) + { + Log.Error(e, "Exception in OnAddonRefresh pre-refresh invoke."); + } + + this.onAddonRefreshHook.Original(atkUnitManager, addon, valueCount, values); + + try + { + this.InvokeListeners(AddonEvent.PostRefresh, new IAddonLifecycle.AddonArgs { Addon = (nint)addon }); + } + catch (Exception e) + { + Log.Error(e, "Exception in OnAddonRefresh post-refresh invoke."); + } + } + private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData) { try diff --git a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs index 557cedc34..079e09c80 100644 --- a/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs +++ b/Dalamud/Game/AddonLifecycle/AddonLifecycleAddressResolver.cs @@ -29,6 +29,11 @@ internal class AddonLifecycleAddressResolver : BaseAddressResolver /// Gets the address of the addon onRequestedUpdate hook invoked by virtual function call. /// public nint AddonOnRequestedUpdate { get; private set; } + + /// + /// Gets the address of AtkUnitManager_vf10 which triggers addon onRefresh. + /// + public nint AddonOnRefresh { get; private set; } /// /// Scan for and setup any configured address pointers. @@ -41,5 +46,6 @@ internal class AddonLifecycleAddressResolver : BaseAddressResolver this.AddonDraw = sig.ScanText("FF 90 ?? ?? ?? ?? 83 EB 01 79 C1"); this.AddonUpdate = sig.ScanText("FF 90 ?? ?? ?? ?? 40 88 AF"); this.AddonOnRequestedUpdate = sig.ScanText("FF 90 90 01 00 00 48 8B 5C 24 30 48 83 C4 20"); + this.AddonOnRefresh = sig.ScanText("48 89 5C 24 08 57 48 83 EC 20 41 8B F8 48 8B DA"); } }