mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 06:13:40 +01:00
merge master
This commit is contained in:
commit
af52da06b0
30 changed files with 827 additions and 301 deletions
|
|
@ -3,20 +3,16 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
|
||||
using Dalamud.Configuration.Internal;
|
||||
using Dalamud.Game.AddonEventManager;
|
||||
using Dalamud.Game.Addon;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Logging.Internal;
|
||||
using Dalamud.Memory;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
using DalamudAddonEventManager = Dalamud.Game.AddonEventManager.AddonEventManager;
|
||||
|
||||
namespace Dalamud.Game.Gui.Dtr;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -27,9 +23,6 @@ namespace Dalamud.Game.Gui.Dtr;
|
|||
internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
||||
{
|
||||
private const uint BaseNodeId = 1000;
|
||||
private const uint MouseOverEventIdOffset = 10000;
|
||||
private const uint MouseOutEventIdOffset = 20000;
|
||||
private const uint MouseClickEventIdOffset = 30000;
|
||||
|
||||
private static readonly ModuleLog Log = new("DtrBar");
|
||||
|
||||
|
|
@ -43,23 +36,29 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly DalamudAddonEventManager uiEventManager = Service<DalamudAddonEventManager>.Get();
|
||||
private readonly AddonEventManager uiEventManager = Service<AddonEventManager>.Get();
|
||||
|
||||
private readonly DtrBarAddressResolver address;
|
||||
[ServiceManager.ServiceDependency]
|
||||
private readonly AddonLifecycle addonLifecycle = Service<AddonLifecycle>.Get();
|
||||
|
||||
private readonly AddonLifecycleEventListener dtrPostDrawListener;
|
||||
private readonly AddonLifecycleEventListener dtrPostRequestedUpdateListener;
|
||||
|
||||
private readonly ConcurrentBag<DtrBarEntry> newEntries = new();
|
||||
private readonly List<DtrBarEntry> entries = new();
|
||||
private readonly Hook<AddonDrawDelegate> onAddonDrawHook;
|
||||
private readonly Hook<AddonRequestedUpdateDelegate> onAddonRequestedUpdateHook;
|
||||
|
||||
private readonly Dictionary<uint, List<IAddonEventHandle>> eventHandles = new();
|
||||
|
||||
private uint runningNodeIds = BaseNodeId;
|
||||
|
||||
[ServiceManager.ServiceConstructor]
|
||||
private DtrBar(SigScanner sigScanner)
|
||||
private DtrBar()
|
||||
{
|
||||
this.address = new DtrBarAddressResolver();
|
||||
this.address.Setup(sigScanner);
|
||||
this.dtrPostDrawListener = new AddonLifecycleEventListener(AddonEvent.PostDraw, "_DTR", this.OnDtrPostDraw);
|
||||
this.dtrPostRequestedUpdateListener = new AddonLifecycleEventListener(AddonEvent.PostRequestedUpdate, "_DTR", this.OnAddonRequestedUpdateDetour);
|
||||
|
||||
this.onAddonDrawHook = Hook<AddonDrawDelegate>.FromAddress(this.address.AtkUnitBaseDraw, this.OnAddonDrawDetour);
|
||||
this.onAddonRequestedUpdateHook = Hook<AddonRequestedUpdateDelegate>.FromAddress(this.address.AddonRequestedUpdate, this.OnAddonRequestedUpdateDetour);
|
||||
this.addonLifecycle.RegisterListener(this.dtrPostDrawListener);
|
||||
this.addonLifecycle.RegisterListener(this.dtrPostRequestedUpdateListener);
|
||||
|
||||
this.framework.Update += this.Update;
|
||||
|
||||
|
|
@ -68,10 +67,6 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
this.configuration.QueueSave();
|
||||
}
|
||||
|
||||
private delegate void AddonDrawDelegate(AtkUnitBase* addon);
|
||||
|
||||
private delegate void AddonRequestedUpdateDelegate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public DtrBarEntry Get(string title, SeString? text = null)
|
||||
{
|
||||
|
|
@ -102,8 +97,8 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
/// <inheritdoc/>
|
||||
void IDisposable.Dispose()
|
||||
{
|
||||
this.onAddonDrawHook.Dispose();
|
||||
this.onAddonRequestedUpdateHook.Dispose();
|
||||
this.addonLifecycle.UnregisterListener(this.dtrPostDrawListener);
|
||||
this.addonLifecycle.UnregisterListener(this.dtrPostRequestedUpdateListener);
|
||||
|
||||
foreach (var entry in this.entries)
|
||||
this.RemoveNode(entry.TextNode);
|
||||
|
|
@ -165,13 +160,6 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
return xPos.CompareTo(yPos);
|
||||
});
|
||||
}
|
||||
|
||||
[ServiceManager.CallWhenServicesReady]
|
||||
private void ContinueConstruction()
|
||||
{
|
||||
this.onAddonDrawHook.Enable();
|
||||
this.onAddonRequestedUpdateHook.Enable();
|
||||
}
|
||||
|
||||
private AtkUnitBase* GetDtr() => (AtkUnitBase*)this.gameGui.GetAddonByName("_DTR").ToPointer();
|
||||
|
||||
|
|
@ -181,7 +169,7 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
this.HandleAddedNodes();
|
||||
|
||||
var dtr = this.GetDtr();
|
||||
if (dtr == null) return;
|
||||
if (dtr == null || dtr->RootNode == null || dtr->RootNode->ChildNode == null) return;
|
||||
|
||||
// The collision node on the DTR element is always the width of its content
|
||||
if (dtr->UldManager.NodeList == null) return;
|
||||
|
|
@ -232,7 +220,7 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
|
||||
if (this.configuration.DtrSwapDirection)
|
||||
{
|
||||
data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2);
|
||||
data.TextNode->AtkResNode.SetPositionFloat(runningXPos + this.configuration.DtrSpacing, 2);
|
||||
runningXPos += elementWidth;
|
||||
}
|
||||
else
|
||||
|
|
@ -241,6 +229,11 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
data.TextNode->AtkResNode.SetPositionFloat(runningXPos, 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we want the node hidden, shift it up, to prevent collision conflicts
|
||||
data.TextNode->AtkResNode.SetY(-collisionNode->Height * dtr->RootNode->ScaleX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,37 +251,26 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
this.ApplySort();
|
||||
}
|
||||
}
|
||||
|
||||
// This hooks all AtkUnitBase.Draw calls, then checks for our specific addon name.
|
||||
// AddonDtr doesn't implement it's own Draw method, would need to replace vtable entry to be more efficient.
|
||||
private void OnAddonDrawDetour(AtkUnitBase* addon)
|
||||
|
||||
private void OnDtrPostDraw(AddonEvent eventType, AddonArgs addonInfo)
|
||||
{
|
||||
this.onAddonDrawHook!.Original(addon);
|
||||
var addon = (AtkUnitBase*)addonInfo.Addon;
|
||||
|
||||
try
|
||||
{
|
||||
if (MemoryHelper.ReadString((nint)addon->Name, 0x20) is not "_DTR") return;
|
||||
|
||||
this.UpdateNodePositions(addon);
|
||||
this.UpdateNodePositions(addon);
|
||||
|
||||
if (!this.configuration.DtrSwapDirection)
|
||||
{
|
||||
var targetSize = (ushort)this.CalculateTotalSize();
|
||||
var sizeDelta = targetSize - addon->RootNode->Width;
|
||||
|
||||
if (addon->RootNode->Width != targetSize)
|
||||
{
|
||||
addon->RootNode->SetWidth(targetSize);
|
||||
addon->SetX((short)(addon->GetX() - sizeDelta));
|
||||
|
||||
// force a RequestedUpdate immediately to force the game to right-justify it immediately.
|
||||
addon->OnUpdate(AtkStage.GetSingleton()->GetNumberArrayData(), AtkStage.GetSingleton()->GetStringArrayData());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
if (!this.configuration.DtrSwapDirection)
|
||||
{
|
||||
Log.Error(e, "Exception in OnAddonDraw.");
|
||||
var targetSize = (ushort)this.CalculateTotalSize();
|
||||
var sizeDelta = MathF.Round((targetSize - addon->RootNode->Width) * addon->RootNode->ScaleX);
|
||||
|
||||
if (addon->RootNode->Width != targetSize)
|
||||
{
|
||||
addon->RootNode->SetWidth(targetSize);
|
||||
addon->SetX((short)(addon->GetX() - sizeDelta));
|
||||
|
||||
// force a RequestedUpdate immediately to force the game to right-justify it immediately.
|
||||
addon->OnUpdate(AtkStage.GetSingleton()->GetNumberArrayData(), AtkStage.GetSingleton()->GetStringArrayData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -315,18 +297,11 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
}
|
||||
}
|
||||
|
||||
private void OnAddonRequestedUpdateDetour(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
|
||||
private void OnAddonRequestedUpdateDetour(AddonEvent eventType, AddonArgs addonInfo)
|
||||
{
|
||||
this.onAddonRequestedUpdateHook.Original(addon, numberArrayData, stringArrayData);
|
||||
|
||||
try
|
||||
{
|
||||
this.UpdateNodePositions(addon);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Exception in OnAddonRequestedUpdate.");
|
||||
}
|
||||
var addon = (AtkUnitBase*)addonInfo.Addon;
|
||||
|
||||
this.UpdateNodePositions(addon);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -350,6 +325,11 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
private void RecreateNodes()
|
||||
{
|
||||
this.runningNodeIds = BaseNodeId;
|
||||
if (this.entries.Any())
|
||||
{
|
||||
this.eventHandles.Clear();
|
||||
}
|
||||
|
||||
foreach (var entry in this.entries)
|
||||
{
|
||||
entry.TextNode = this.MakeNode(++this.runningNodeIds);
|
||||
|
|
@ -384,10 +364,14 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
var dtr = this.GetDtr();
|
||||
if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return false;
|
||||
|
||||
this.uiEventManager.AddEvent(node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler);
|
||||
this.uiEventManager.AddEvent(node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler);
|
||||
this.uiEventManager.AddEvent(node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler);
|
||||
|
||||
this.eventHandles.TryAdd(node->AtkResNode.NodeID, new List<IAddonEventHandle>());
|
||||
this.eventHandles[node->AtkResNode.NodeID].AddRange(new List<IAddonEventHandle>
|
||||
{
|
||||
this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOver, this.DtrEventHandler),
|
||||
this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseOut, this.DtrEventHandler),
|
||||
this.uiEventManager.AddEvent(AddonEventManager.DalamudInternalKey, (nint)dtr, (nint)node, AddonEventType.MouseClick, this.DtrEventHandler),
|
||||
});
|
||||
|
||||
var lastChild = dtr->RootNode->ChildNode;
|
||||
while (lastChild->PrevSiblingNode != null) lastChild = lastChild->PrevSiblingNode;
|
||||
Log.Debug($"Found last sibling: {(ulong)lastChild:X}");
|
||||
|
|
@ -404,14 +388,13 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool RemoveNode(AtkTextNode* node)
|
||||
private void RemoveNode(AtkTextNode* node)
|
||||
{
|
||||
var dtr = this.GetDtr();
|
||||
if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return false;
|
||||
if (dtr == null || dtr->RootNode == null || dtr->UldManager.NodeList == null || node == null) return;
|
||||
|
||||
this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseOverEventIdOffset, (nint)node, AddonEventType.MouseOver);
|
||||
this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseOutEventIdOffset, (nint)node, AddonEventType.MouseOut);
|
||||
this.uiEventManager.RemoveEvent(node->AtkResNode.NodeID + MouseClickEventIdOffset, (nint)node, AddonEventType.MouseClick);
|
||||
this.eventHandles[node->AtkResNode.NodeID].ForEach(handle => this.uiEventManager.RemoveEvent(AddonEventManager.DalamudInternalKey, handle));
|
||||
this.eventHandles[node->AtkResNode.NodeID].Clear();
|
||||
|
||||
var tmpPrevNode = node->AtkResNode.PrevSiblingNode;
|
||||
var tmpNextNode = node->AtkResNode.NextSiblingNode;
|
||||
|
|
@ -427,7 +410,6 @@ internal sealed unsafe class DtrBar : IDisposable, IServiceType, IDtrBar
|
|||
dtr->UldManager.UpdateDrawNodeList();
|
||||
dtr->UpdateCollisionNodeList(false);
|
||||
Log.Debug("Updated node draw list");
|
||||
return true;
|
||||
}
|
||||
|
||||
private AtkTextNode* MakeNode(uint nodeId)
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
namespace Dalamud.Game.Gui.Dtr;
|
||||
|
||||
/// <summary>
|
||||
/// DtrBar memory address resolver.
|
||||
/// </summary>
|
||||
internal class DtrBarAddressResolver : BaseAddressResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the address of the AtkUnitBaseDraw method.
|
||||
/// This is the base handler for all addons.
|
||||
/// We will use this here because _DTR does not have a overloaded handler, so we must use the base handler.
|
||||
/// </summary>
|
||||
public nint AtkUnitBaseDraw { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the DTRRequestUpdate method.
|
||||
/// </summary>
|
||||
public nint AddonRequestedUpdate { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scan for and setup any configured address pointers.
|
||||
/// </summary>
|
||||
/// <param name="scanner">The signature scanner to facilitate setup.</param>
|
||||
protected override void Setup64Bit(SigScanner scanner)
|
||||
{
|
||||
this.AtkUnitBaseDraw = scanner.ScanText("48 83 EC 28 F6 81 ?? ?? ?? ?? ?? 4C 8B C1");
|
||||
this.AddonRequestedUpdate = scanner.ScanText("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B BA ?? ?? ?? ?? 48 8B F1 49 8B 98 ?? ?? ?? ?? 33 D2");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue