mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-02 13:53:40 +01:00
Merge branch 'api13' into imgui-bindings
This commit is contained in:
commit
7e5485f42d
64 changed files with 1351 additions and 911 deletions
|
|
@ -153,23 +153,11 @@ internal sealed class DalamudConfiguration : IInternalDisposableService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float GlobalUiScale { get; set; } = 1.0f;
|
public float GlobalUiScale { get; set; } = 1.0f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value indicating whether to use AXIS fonts from the game.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete($"See {nameof(DefaultFontSpec)}")]
|
|
||||||
public bool UseAxisFontsFromGame { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the default font spec.
|
/// Gets or sets the default font spec.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IFontSpec? DefaultFontSpec { get; set; }
|
public IFontSpec? DefaultFontSpec { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the gamma value to apply for Dalamud fonts. Do not use.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("It happens that nobody touched this setting", true)]
|
|
||||||
public float FontGammaLevel { get; set; } = 1.4f;
|
|
||||||
|
|
||||||
/// <summary>Gets or sets the opacity of the IME state indicator.</summary>
|
/// <summary>Gets or sets the opacity of the IME state indicator.</summary>
|
||||||
/// <value>0 will hide the state indicator. 1 will make the state indicator fully visible. Values outside the
|
/// <value>0 will hide the state indicator. 1 will make the state indicator fully visible. Values outside the
|
||||||
/// range will be clamped to [0, 1].</value>
|
/// range will be clamped to [0, 1].</value>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<PropertyGroup Label="Feature">
|
<PropertyGroup Label="Feature">
|
||||||
<Description>XIV Launcher addon framework</Description>
|
<Description>XIV Launcher addon framework</Description>
|
||||||
<DalamudVersion>12.0.1.5</DalamudVersion>
|
<DalamudVersion>13.0.0.0</DalamudVersion>
|
||||||
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
<AssemblyVersion>$(DalamudVersion)</AssemblyVersion>
|
||||||
<Version>$(DalamudVersion)</Version>
|
<Version>$(DalamudVersion)</Version>
|
||||||
<FileVersion>$(DalamudVersion)</FileVersion>
|
<FileVersion>$(DalamudVersion)</FileVersion>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
@ -33,17 +33,10 @@ internal unsafe class AddonEventEntry
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public required nint Node { get; init; }
|
public required nint Node { get; init; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the handler that gets called when this event is triggered.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use AddonEventDelegate Delegate instead")]
|
|
||||||
public IAddonEventManager.AddonEventHandler Handler { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the delegate that gets called when this event is triggered.
|
/// Gets the delegate that gets called when this event is triggered.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Api13ToDo("Make this field required")]
|
public required IAddonEventManager.AddonEventDelegate Delegate { get; init; }
|
||||||
public IAddonEventManager.AddonEventDelegate Delegate { get; init; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the unique id for this event.
|
/// Gets the unique id for this event.
|
||||||
|
|
|
||||||
|
|
@ -73,29 +73,6 @@ internal unsafe class AddonEventManager : IInternalDisposableService
|
||||||
this.addonLifecycle.UnregisterListener(this.finalizeEventListener);
|
this.addonLifecycle.UnregisterListener(this.finalizeEventListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers an event handler for the specified addon, node, and type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="pluginId">Unique ID for this plugin.</param>
|
|
||||||
/// <param name="atkUnitBase">The parent addon for this event.</param>
|
|
||||||
/// <param name="atkResNode">The node that will trigger this event.</param>
|
|
||||||
/// <param name="eventType">The event type for this event.</param>
|
|
||||||
/// <param name="eventHandler">The handler to call when event is triggered.</param>
|
|
||||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
|
||||||
internal IAddonEventHandle? AddEvent(Guid pluginId, IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
|
||||||
{
|
|
||||||
if (this.pluginEventControllers.TryGetValue(pluginId, out var controller))
|
|
||||||
{
|
|
||||||
return controller.AddEvent(atkUnitBase, atkResNode, eventType, eventHandler);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Verbose($"Unable to locate controller for {pluginId}. No event was added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an event handler for the specified addon, node, and type.
|
/// Registers an event handler for the specified addon, node, and type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -260,10 +237,6 @@ internal class AddonEventManagerPluginScoped : IInternalDisposableService, IAddo
|
||||||
}).Wait();
|
}).Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IAddonEventHandle? AddEvent(IntPtr atkUnitBase, IntPtr atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventHandler eventHandler)
|
|
||||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventHandler);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
public IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, IAddonEventManager.AddonEventDelegate eventDelegate)
|
||||||
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventDelegate);
|
=> this.eventManagerService.AddEvent(this.plugin.EffectiveWorkingPluginId, atkUnitBase, atkResNode, eventType, eventDelegate);
|
||||||
|
|
|
||||||
|
|
@ -121,12 +121,6 @@ public enum AddonEventType : byte
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ListItemClick = 35,
|
ListItemClick = 35,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AtkComponentList Toggle.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use ListItemClick")]
|
|
||||||
ListItemToggle = 35,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// AtkComponentList Double Click.
|
/// AtkComponentList Double Click.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,32 @@
|
||||||
namespace Dalamud.Game.Addon.Events;
|
namespace Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Object representing data that is relevant in handling native events.
|
/// Object representing data that is relevant in handling native events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AddonEventData
|
public class AddonEventData
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddonEventData"/> class.
|
||||||
|
/// </summary>
|
||||||
|
internal AddonEventData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddonEventData"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventData">Other event data to copy.</param>
|
||||||
|
internal AddonEventData(AddonEventData eventData)
|
||||||
|
{
|
||||||
|
this.AtkEventType = eventData.AtkEventType;
|
||||||
|
this.Param = eventData.Param;
|
||||||
|
this.AtkEventPointer = eventData.AtkEventPointer;
|
||||||
|
this.AtkEventDataPointer = eventData.AtkEventDataPointer;
|
||||||
|
this.AddonPointer = eventData.AddonPointer;
|
||||||
|
this.NodeTargetPointer = eventData.NodeTargetPointer;
|
||||||
|
this.AtkEventListener = eventData.AtkEventListener;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the AtkEventType for this event.
|
/// Gets the AtkEventType for this event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -18,8 +40,8 @@ public class AddonEventData
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the pointer to the AtkEvent object for this event.
|
/// Gets the pointer to the AtkEvent object for this event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>Note: This is not a pointer to the AtkEventData object.<br/><br/></remarks>
|
/// <remarks>Note: This is not a pointer to the AtkEventData object.<br/><br/>
|
||||||
/// <remarks>Warning: AtkEvent->Node has been modified to be the AtkUnitBase*, and AtkEvent->Target has been modified to be the AtkResNode* that triggered this event.</remarks>
|
/// Warning: AtkEvent->Node has been modified to be the AtkUnitBase*, and AtkEvent->Target has been modified to be the AtkResNode* that triggered this event.</remarks>
|
||||||
public nint AtkEventPointer { get; internal set; }
|
public nint AtkEventPointer { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
using AtkMouseData = FFXIVClientStructs.FFXIV.Component.GUI.AtkEventData.AtkMouseData;
|
||||||
|
using ModifierFlag = FFXIVClientStructs.FFXIV.Component.GUI.AtkEventData.AtkMouseData.ModifierFlag;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public unsafe class AddonMouseEventData : AddonEventData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddonMouseEventData"/> class.
|
||||||
|
/// </summary>
|
||||||
|
internal AddonMouseEventData()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddonMouseEventData"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventData">Other event data to copy.</param>
|
||||||
|
internal AddonMouseEventData(AddonEventData eventData)
|
||||||
|
: base(eventData)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the event was a Left Mouse Click.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLeftClick => this.MouseData.ButtonId is 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the event was a Right Mouse Click.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRightClick => this.MouseData.ButtonId is 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether there are any modifiers set such as alt, control, shift, or dragging.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsNoModifier => this.MouseData.Modifier is 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether alt was being held when this event triggered.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsAltHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Alt);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether control was being held when this event triggered.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsControlHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Ctrl);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether shift was being held when this event triggered.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsShiftHeld => this.MouseData.Modifier.HasFlag(ModifierFlag.Shift);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this event is a mouse drag or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDragging => this.MouseData.Modifier.HasFlag(ModifierFlag.Dragging);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the event was a scroll up.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsScrollUp => this.MouseData.WheelDirection is 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the event was a scroll down.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsScrollDown => this.MouseData.WheelDirection is -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position of the mouse when this event was triggered.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Position => new(this.MouseData.PosX, this.MouseData.PosY);
|
||||||
|
|
||||||
|
private AtkEventData* AtkEventData => (AtkEventData*)this.AtkEventDataPointer;
|
||||||
|
|
||||||
|
private AtkMouseData MouseData => this.AtkEventData->MouseData;
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
|
@ -29,49 +30,6 @@ internal unsafe class PluginEventController : IDisposable
|
||||||
|
|
||||||
private List<AddonEventEntry> Events { get; } = new();
|
private List<AddonEventEntry> Events { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a tracked event.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="atkUnitBase">The Parent addon for the event.</param>
|
|
||||||
/// <param name="atkResNode">The Node for the event.</param>
|
|
||||||
/// <param name="atkEventType">The Event Type.</param>
|
|
||||||
/// <param name="handler">The delegate to call when invoking this event.</param>
|
|
||||||
/// <returns>IAddonEventHandle used to remove the event.</returns>
|
|
||||||
[Obsolete("Use AddEvent that uses AddonEventDelegate instead of AddonEventHandler")]
|
|
||||||
public IAddonEventHandle AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType atkEventType, IAddonEventManager.AddonEventHandler handler)
|
|
||||||
{
|
|
||||||
var node = (AtkResNode*)atkResNode;
|
|
||||||
var addon = (AtkUnitBase*)atkUnitBase;
|
|
||||||
var eventType = (AtkEventType)atkEventType;
|
|
||||||
var eventId = this.GetNextParamKey();
|
|
||||||
var eventGuid = Guid.NewGuid();
|
|
||||||
|
|
||||||
var eventHandle = new AddonEventHandle
|
|
||||||
{
|
|
||||||
AddonName = addon->NameString,
|
|
||||||
ParamKey = eventId,
|
|
||||||
EventType = atkEventType,
|
|
||||||
EventGuid = eventGuid,
|
|
||||||
};
|
|
||||||
|
|
||||||
var eventEntry = new AddonEventEntry
|
|
||||||
{
|
|
||||||
Addon = atkUnitBase,
|
|
||||||
Handler = handler,
|
|
||||||
Delegate = null,
|
|
||||||
Node = atkResNode,
|
|
||||||
EventType = atkEventType,
|
|
||||||
ParamKey = eventId,
|
|
||||||
Handle = eventHandle,
|
|
||||||
};
|
|
||||||
|
|
||||||
Log.Verbose($"Adding Event. {eventEntry.LogString}");
|
|
||||||
this.EventListener.RegisterEvent(addon, node, eventType, eventId);
|
|
||||||
this.Events.Add(eventEntry);
|
|
||||||
|
|
||||||
return eventHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a tracked event.
|
/// Adds a tracked event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -100,7 +58,6 @@ internal unsafe class PluginEventController : IDisposable
|
||||||
{
|
{
|
||||||
Addon = atkUnitBase,
|
Addon = atkUnitBase,
|
||||||
Delegate = eventDelegate,
|
Delegate = eventDelegate,
|
||||||
Handler = null,
|
|
||||||
Node = atkResNode,
|
Node = atkResNode,
|
||||||
EventType = atkEventType,
|
EventType = atkEventType,
|
||||||
ParamKey = eventId,
|
ParamKey = eventId,
|
||||||
|
|
@ -184,7 +141,7 @@ internal unsafe class PluginEventController : IDisposable
|
||||||
if (currentAddonPointer != eventEntry.Addon) return;
|
if (currentAddonPointer != eventEntry.Addon) return;
|
||||||
|
|
||||||
// Make sure the addon is not unloaded
|
// Make sure the addon is not unloaded
|
||||||
var atkUnitBase = (AtkUnitBase*)currentAddonPointer;
|
var atkUnitBase = currentAddonPointer.Struct;
|
||||||
if (atkUnitBase->UldManager.LoadedState == AtkLoadState.Unloaded) return;
|
if (atkUnitBase->UldManager.LoadedState == AtkLoadState.Unloaded) return;
|
||||||
|
|
||||||
// Does this addon contain the node this event is for? (by address)
|
// Does this addon contain the node this event is for? (by address)
|
||||||
|
|
@ -230,7 +187,6 @@ internal unsafe class PluginEventController : IDisposable
|
||||||
this.EventListener.UnregisterEvent(atkResNode, eventType, eventEntry.ParamKey);
|
this.EventListener.UnregisterEvent(atkResNode, eventType, eventEntry.ParamKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Api13ToDo("Remove invoke from eventInfo.Handler, and remove nullability from eventInfo.Delegate?.Invoke")]
|
|
||||||
private void PluginEventListHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventPtr, AtkEventData* eventDataPtr)
|
private void PluginEventListHandler(AtkEventListener* self, AtkEventType eventType, uint eventParam, AtkEvent* eventPtr, AtkEventData* eventDataPtr)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -238,10 +194,7 @@ internal unsafe class PluginEventController : IDisposable
|
||||||
if (eventPtr is null) return;
|
if (eventPtr is null) return;
|
||||||
if (this.Events.FirstOrDefault(handler => handler.ParamKey == eventParam) is not { } eventInfo) return;
|
if (this.Events.FirstOrDefault(handler => handler.ParamKey == eventParam) is not { } eventInfo) return;
|
||||||
|
|
||||||
// We stored the AtkUnitBase* in EventData->Node, and EventData->Target contains the node that triggered the event.
|
eventInfo.Delegate.Invoke((AddonEventType)eventType, new AddonEventData
|
||||||
eventInfo.Handler?.Invoke((AddonEventType)eventType, (nint)eventPtr->Node, (nint)eventPtr->Target);
|
|
||||||
|
|
||||||
eventInfo.Delegate?.Invoke((AddonEventType)eventType, new AddonEventData
|
|
||||||
{
|
{
|
||||||
AddonPointer = (nint)eventPtr->Node,
|
AddonPointer = (nint)eventPtr->Node,
|
||||||
NodeTargetPointer = (nint)eventPtr->Target,
|
NodeTargetPointer = (nint)eventPtr->Target,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
using System.Runtime.CompilerServices;
|
using Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
using Dalamud.Memory;
|
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
||||||
|
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
|
|
@ -17,7 +13,6 @@ public abstract unsafe class AddonArgs
|
||||||
public const string InvalidAddon = "NullAddon";
|
public const string InvalidAddon = "NullAddon";
|
||||||
|
|
||||||
private string? addonName;
|
private string? addonName;
|
||||||
private IntPtr addon;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the addon this args referrers to.
|
/// Gets the name of the addon this args referrers to.
|
||||||
|
|
@ -27,10 +22,10 @@ public abstract unsafe class AddonArgs
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the pointer to the addons AtkUnitBase.
|
/// Gets the pointer to the addons AtkUnitBase.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public nint Addon
|
public AtkUnitBasePtr Addon
|
||||||
{
|
{
|
||||||
get => this.AddonInternal;
|
get;
|
||||||
init => this.AddonInternal = value;
|
internal set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -38,22 +33,6 @@ public abstract unsafe class AddonArgs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract AddonArgsType Type { get; }
|
public abstract AddonArgsType Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the pointer to the addons AtkUnitBase.
|
|
||||||
/// </summary>
|
|
||||||
internal nint AddonInternal
|
|
||||||
{
|
|
||||||
get => this.addon;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this.addon = value;
|
|
||||||
|
|
||||||
// Note: always clear addonName on updating the addon being pointed.
|
|
||||||
// Same address may point to a different addon.
|
|
||||||
this.addonName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if addon name matches the given span of char.
|
/// Checks if addon name matches the given span of char.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -61,19 +40,27 @@ public abstract unsafe class AddonArgs
|
||||||
/// <returns>Whether it is the case.</returns>
|
/// <returns>Whether it is the case.</returns>
|
||||||
internal bool IsAddon(ReadOnlySpan<char> name)
|
internal bool IsAddon(ReadOnlySpan<char> name)
|
||||||
{
|
{
|
||||||
if (this.Addon == nint.Zero) return false;
|
if (this.Addon.IsNull)
|
||||||
if (name.Length is 0 or > 0x20)
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var addonPointer = (AtkUnitBase*)this.Addon;
|
if (name.Length is 0 or > 32)
|
||||||
if (addonPointer->Name[0] == 0) return false;
|
return false;
|
||||||
|
|
||||||
// note: might want to rewrite this to just compare to NameString
|
var addonName = this.Addon.Name;
|
||||||
return MemoryHelper.EqualsZeroTerminatedString(
|
|
||||||
name,
|
if (string.IsNullOrEmpty(addonName))
|
||||||
(nint)Unsafe.AsPointer(ref addonPointer->Name[0]),
|
return false;
|
||||||
null,
|
|
||||||
0x20);
|
return name == addonName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears this AddonArgs values.
|
||||||
|
/// </summary>
|
||||||
|
internal virtual void Clear()
|
||||||
|
{
|
||||||
|
this.addonName = null;
|
||||||
|
this.Addon = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -82,11 +69,13 @@ public abstract unsafe class AddonArgs
|
||||||
/// <returns>The name of the addon for this object. <see cref="InvalidAddon"/> when invalid.</returns>
|
/// <returns>The name of the addon for this object. <see cref="InvalidAddon"/> when invalid.</returns>
|
||||||
private string GetAddonName()
|
private string GetAddonName()
|
||||||
{
|
{
|
||||||
if (this.Addon == nint.Zero) return InvalidAddon;
|
if (this.Addon.IsNull) return InvalidAddon;
|
||||||
|
|
||||||
var addonPointer = (AtkUnitBase*)this.Addon;
|
var name = this.Addon.Name;
|
||||||
if (addonPointer->Name[0] == 0) return InvalidAddon;
|
|
||||||
|
|
||||||
return this.addonName ??= addonPointer->NameString;
|
if (string.IsNullOrEmpty(name))
|
||||||
|
return InvalidAddon;
|
||||||
|
|
||||||
|
return this.addonName ??= name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,14 @@ public class AddonReceiveEventArgs : AddonArgs, ICloneable
|
||||||
|
|
||||||
/// <inheritdoc cref="Clone"/>
|
/// <inheritdoc cref="Clone"/>
|
||||||
object ICloneable.Clone() => this.Clone();
|
object ICloneable.Clone() => this.Clone();
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||||
|
internal override void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
this.AtkEventType = default;
|
||||||
|
this.EventParam = default;
|
||||||
|
this.AtkEvent = default;
|
||||||
|
this.Data = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,12 @@ public class AddonRefreshArgs : AddonArgs, ICloneable
|
||||||
|
|
||||||
/// <inheritdoc cref="Clone"/>
|
/// <inheritdoc cref="Clone"/>
|
||||||
object ICloneable.Clone() => this.Clone();
|
object ICloneable.Clone() => this.Clone();
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||||
|
internal override void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
this.AtkValueCount = default;
|
||||||
|
this.AtkValues = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for OnRequestedUpdate events.
|
/// Addon argument data for OnRequestedUpdate events.
|
||||||
|
|
@ -31,4 +31,12 @@ public class AddonRequestedUpdateArgs : AddonArgs, ICloneable
|
||||||
|
|
||||||
/// <inheritdoc cref="Clone"/>
|
/// <inheritdoc cref="Clone"/>
|
||||||
object ICloneable.Clone() => this.Clone();
|
object ICloneable.Clone() => this.Clone();
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||||
|
internal override void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
this.NumberArrayData = default;
|
||||||
|
this.StringArrayData = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
namespace Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
|
|
||||||
|
|
@ -38,4 +38,12 @@ public class AddonSetupArgs : AddonArgs, ICloneable
|
||||||
|
|
||||||
/// <inheritdoc cref="Clone"/>
|
/// <inheritdoc cref="Clone"/>
|
||||||
object ICloneable.Clone() => this.Clone();
|
object ICloneable.Clone() => this.Clone();
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||||
|
internal override void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
this.AtkValueCount = default;
|
||||||
|
this.AtkValues = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,4 +35,11 @@ public class AddonUpdateArgs : AddonArgs, ICloneable
|
||||||
|
|
||||||
/// <inheritdoc cref="Clone"/>
|
/// <inheritdoc cref="Clone"/>
|
||||||
object ICloneable.Clone() => this.Clone();
|
object ICloneable.Clone() => this.Clone();
|
||||||
|
|
||||||
|
/// <inheritdoc cref="AddonArgs.Clear"/>
|
||||||
|
internal override void Clear()
|
||||||
|
{
|
||||||
|
base.Clear();
|
||||||
|
this.TimeDeltaInternal = default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
}
|
}
|
||||||
|
|
||||||
using var returner = this.argsPool.Rent(out AddonSetupArgs arg);
|
using var returner = this.argsPool.Rent(out AddonSetupArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
arg.AtkValueCount = valueCount;
|
arg.AtkValueCount = valueCount;
|
||||||
arg.AtkValues = (nint)values;
|
arg.AtkValues = (nint)values;
|
||||||
this.InvokeListenersSafely(AddonEvent.PreSetup, arg);
|
this.InvokeListenersSafely(AddonEvent.PreSetup, arg);
|
||||||
|
|
@ -270,7 +271,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
}
|
}
|
||||||
|
|
||||||
using var returner = this.argsPool.Rent(out AddonFinalizeArgs arg);
|
using var returner = this.argsPool.Rent(out AddonFinalizeArgs arg);
|
||||||
arg.AddonInternal = (nint)atkUnitBase[0];
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)atkUnitBase[0];
|
||||||
this.InvokeListenersSafely(AddonEvent.PreFinalize, arg);
|
this.InvokeListenersSafely(AddonEvent.PreFinalize, arg);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -286,7 +288,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
private void OnAddonDraw(AtkUnitBase* addon)
|
private void OnAddonDraw(AtkUnitBase* addon)
|
||||||
{
|
{
|
||||||
using var returner = this.argsPool.Rent(out AddonDrawArgs arg);
|
using var returner = this.argsPool.Rent(out AddonDrawArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
this.InvokeListenersSafely(AddonEvent.PreDraw, arg);
|
this.InvokeListenersSafely(AddonEvent.PreDraw, arg);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|
@ -304,7 +307,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
private void OnAddonUpdate(AtkUnitBase* addon, float delta)
|
private void OnAddonUpdate(AtkUnitBase* addon, float delta)
|
||||||
{
|
{
|
||||||
using var returner = this.argsPool.Rent(out AddonUpdateArgs arg);
|
using var returner = this.argsPool.Rent(out AddonUpdateArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
arg.TimeDeltaInternal = delta;
|
arg.TimeDeltaInternal = delta;
|
||||||
this.InvokeListenersSafely(AddonEvent.PreUpdate, arg);
|
this.InvokeListenersSafely(AddonEvent.PreUpdate, arg);
|
||||||
|
|
||||||
|
|
@ -325,7 +329,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
var result = false;
|
var result = false;
|
||||||
|
|
||||||
using var returner = this.argsPool.Rent(out AddonRefreshArgs arg);
|
using var returner = this.argsPool.Rent(out AddonRefreshArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
arg.AtkValueCount = valueCount;
|
arg.AtkValueCount = valueCount;
|
||||||
arg.AtkValues = (nint)values;
|
arg.AtkValues = (nint)values;
|
||||||
this.InvokeListenersSafely(AddonEvent.PreRefresh, arg);
|
this.InvokeListenersSafely(AddonEvent.PreRefresh, arg);
|
||||||
|
|
@ -348,7 +353,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
|
private void OnRequestedUpdate(AtkUnitBase* addon, NumberArrayData** numberArrayData, StringArrayData** stringArrayData)
|
||||||
{
|
{
|
||||||
using var returner = this.argsPool.Rent(out AddonRequestedUpdateArgs arg);
|
using var returner = this.argsPool.Rent(out AddonRequestedUpdateArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
arg.NumberArrayData = (nint)numberArrayData;
|
arg.NumberArrayData = (nint)numberArrayData;
|
||||||
arg.StringArrayData = (nint)stringArrayData;
|
arg.StringArrayData = (nint)stringArrayData;
|
||||||
this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, arg);
|
this.InvokeListenersSafely(AddonEvent.PreRequestedUpdate, arg);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
|
|
@ -86,7 +86,8 @@ internal unsafe class AddonLifecycleReceiveEventListener : IDisposable
|
||||||
}
|
}
|
||||||
|
|
||||||
using var returner = this.argsPool.Rent(out AddonReceiveEventArgs arg);
|
using var returner = this.argsPool.Rent(out AddonReceiveEventArgs arg);
|
||||||
arg.AddonInternal = (nint)addon;
|
arg.Clear();
|
||||||
|
arg.Addon = (nint)addon;
|
||||||
arg.AtkEventType = (byte)eventType;
|
arg.AtkEventType = (byte)eventType;
|
||||||
arg.EventParam = eventParam;
|
arg.EventParam = eventParam;
|
||||||
arg.AtkEvent = (IntPtr)atkEvent;
|
arg.AtkEvent = (IntPtr)atkEvent;
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,6 @@ internal partial class ChatHandlers : IServiceType
|
||||||
if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Util.AssemblyVersion.StartsWith(this.configuration.LastVersion))
|
if (string.IsNullOrEmpty(this.configuration.LastVersion) || !Util.AssemblyVersion.StartsWith(this.configuration.LastVersion))
|
||||||
{
|
{
|
||||||
var linkPayload = chatGui.AddChatLinkHandler(
|
var linkPayload = chatGui.AddChatLinkHandler(
|
||||||
"dalamud",
|
|
||||||
8459324,
|
|
||||||
(_, _) => dalamudInterface.OpenPluginInstallerTo(PluginInstallerOpenKind.Changelogs));
|
(_, _) => dalamudInterface.OpenPluginInstallerTo(PluginInstallerOpenKind.Changelogs));
|
||||||
|
|
||||||
var updateMessage = new SeStringBuilder()
|
var updateMessage = new SeStringBuilder()
|
||||||
|
|
|
||||||
|
|
@ -188,12 +188,6 @@ public enum ConditionFlag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ExecutingCraftingAction = 40,
|
ExecutingCraftingAction = 40,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command while crafting.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Renamed to ExecutingCraftingAction.")]
|
|
||||||
Crafting40 = 40,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command while preparing to craft.
|
/// Unable to execute command while preparing to craft.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -205,12 +199,6 @@ public enum ConditionFlag
|
||||||
/// <remarks> Includes fishing. </remarks>
|
/// <remarks> Includes fishing. </remarks>
|
||||||
ExecutingGatheringAction = 42,
|
ExecutingGatheringAction = 42,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command while gathering.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Renamed to ExecutingGatheringAction.")]
|
|
||||||
Gathering42 = 42,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command while fishing.
|
/// Unable to execute command while fishing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -235,12 +223,6 @@ public enum ConditionFlag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Jumping = 48,
|
Jumping = 48,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command while auto-run is active.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("To avoid confusion, renamed to UsingChocoboTaxi.")]
|
|
||||||
AutorunActive = 49,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command while auto-run is active.
|
/// Unable to execute command while auto-run is active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -282,12 +264,6 @@ public enum ConditionFlag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BoundByDuty56 = 56,
|
BoundByDuty56 = 56,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command at this time.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Renamed to MountOrOrnamentTransition.")]
|
|
||||||
Unknown57 = 57,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command at this time.
|
/// Unable to execute command at this time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -461,12 +437,6 @@ public enum ConditionFlag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
RolePlaying = 90,
|
RolePlaying = 90,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command while bound by duty.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use InDutyQueue")]
|
|
||||||
BoundToDuty97 = 91,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command while bound by duty.
|
/// Unable to execute command while bound by duty.
|
||||||
/// Specifically triggered when you are in a queue for a duty but not inside a duty.
|
/// Specifically triggered when you are in a queue for a duty but not inside a duty.
|
||||||
|
|
@ -483,12 +453,6 @@ public enum ConditionFlag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
WaitingToVisitOtherWorld = 93,
|
WaitingToVisitOtherWorld = 93,
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unable to execute command while using a parasol.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Renamed to UsingFashionAccessory.")]
|
|
||||||
UsingParasol = 94,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unable to execute command while using a fashion accessory.
|
/// Unable to execute command while using a fashion accessory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using System.Numerics;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using Dalamud.Utility;
|
|
||||||
|
|
||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
|
|
||||||
|
|
@ -69,12 +68,6 @@ public interface IFate : IEquatable<IFate>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte Progress { get; }
|
byte Progress { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this <see cref="Fate"/> has a EXP bonus.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete($"Use {nameof(HasBonus)} instead")]
|
|
||||||
bool HasExpBonus { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this <see cref="Fate"/> has a bonus.
|
/// Gets a value indicating whether this <see cref="Fate"/> has a bonus.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -222,10 +215,6 @@ internal unsafe partial class Fate : IFate
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public byte Progress => this.Struct->Progress;
|
public byte Progress => this.Struct->Progress;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
[Api13ToDo("Remove")]
|
|
||||||
public bool HasExpBonus => this.HasBonus;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool HasBonus => this.Struct->IsBonus;
|
public bool HasBonus => this.Struct->IsBonus;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Plugin.Internal.Types;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
|
@ -40,7 +41,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
private static readonly ModuleLog Log = new("ChatGui");
|
private static readonly ModuleLog Log = new("ChatGui");
|
||||||
|
|
||||||
private readonly Queue<XivChatEntry> chatQueue = new();
|
private readonly Queue<XivChatEntry> chatQueue = new();
|
||||||
private readonly Dictionary<(string PluginName, uint CommandId), Action<uint, SeString>> dalamudLinkHandlers = new();
|
private readonly Dictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> dalamudLinkHandlers = new();
|
||||||
|
|
||||||
private readonly Hook<PrintMessageDelegate> printMessageHook;
|
private readonly Hook<PrintMessageDelegate> printMessageHook;
|
||||||
private readonly Hook<InventoryItem.Delegates.Copy> inventoryItemCopyHook;
|
private readonly Hook<InventoryItem.Delegates.Copy> inventoryItemCopyHook;
|
||||||
|
|
@ -49,7 +50,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
|
|
||||||
private ImmutableDictionary<(string PluginName, uint CommandId), Action<uint, SeString>>? dalamudLinkHandlersCopy;
|
private ImmutableDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>>? dalamudLinkHandlersCopy;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private ChatGui()
|
private ChatGui()
|
||||||
|
|
@ -85,7 +86,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
public byte LastLinkedItemFlags { get; private set; }
|
public byte LastLinkedItemFlags { get; private set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers
|
public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> RegisteredLinkHandlers
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
|
@ -161,6 +162,28 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Chat Links
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public DalamudLinkPayload AddChatLinkHandler(Action<Guid, SeString> commandAction)
|
||||||
|
{
|
||||||
|
return this.AddChatLinkHandler("Dalamud", commandAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveChatLinkHandler(Guid commandId)
|
||||||
|
{
|
||||||
|
this.RemoveChatLinkHandler("Dalamud", commandId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveChatLinkHandler()
|
||||||
|
{
|
||||||
|
this.RemoveChatLinkHandler("Dalamud");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Process a chat queue.
|
/// Process a chat queue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -217,12 +240,11 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
/// Create a link handler.
|
/// Create a link handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
||||||
/// <param name="commandId">The ID of the command to run.</param>
|
|
||||||
/// <param name="commandAction">The command action itself.</param>
|
/// <param name="commandAction">The command action itself.</param>
|
||||||
/// <returns>A payload for handling.</returns>
|
/// <returns>A payload for handling.</returns>
|
||||||
[Api13ToDo("Plugins should not specify their own command IDs here. We should assign them ourselves.")]
|
internal DalamudLinkPayload AddChatLinkHandler(string pluginName, Action<Guid, SeString> commandAction)
|
||||||
internal DalamudLinkPayload AddChatLinkHandler(string pluginName, uint commandId, Action<uint, SeString> commandAction)
|
|
||||||
{
|
{
|
||||||
|
var commandId = Guid.CreateVersion7();
|
||||||
var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId };
|
var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId };
|
||||||
lock (this.dalamudLinkHandlers)
|
lock (this.dalamudLinkHandlers)
|
||||||
{
|
{
|
||||||
|
|
@ -255,7 +277,7 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
/// <param name="pluginName">The name of the plugin handling the link.</param>
|
||||||
/// <param name="commandId">The ID of the command to be removed.</param>
|
/// <param name="commandId">The ID of the command to be removed.</param>
|
||||||
internal void RemoveChatLinkHandler(string pluginName, uint commandId)
|
internal void RemoveChatLinkHandler(string pluginName, Guid commandId)
|
||||||
{
|
{
|
||||||
lock (this.dalamudLinkHandlers)
|
lock (this.dalamudLinkHandlers)
|
||||||
{
|
{
|
||||||
|
|
@ -478,11 +500,15 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly ChatGui chatGuiService = Service<ChatGui>.Get();
|
private readonly ChatGui chatGuiService = Service<ChatGui>.Get();
|
||||||
|
|
||||||
|
private readonly LocalPlugin plugin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ChatGuiPluginScoped"/> class.
|
/// Initializes a new instance of the <see cref="ChatGuiPluginScoped"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ChatGuiPluginScoped()
|
/// <param name="plugin">The plugin.</param>
|
||||||
|
internal ChatGuiPluginScoped(LocalPlugin plugin)
|
||||||
{
|
{
|
||||||
|
this.plugin = plugin;
|
||||||
this.chatGuiService.ChatMessage += this.OnMessageForward;
|
this.chatGuiService.ChatMessage += this.OnMessageForward;
|
||||||
this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward;
|
this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward;
|
||||||
this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward;
|
this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward;
|
||||||
|
|
@ -508,7 +534,7 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
||||||
public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags;
|
public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers;
|
public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> RegisteredLinkHandlers => this.chatGuiService.RegisteredLinkHandlers;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
void IInternalDisposableService.DisposeService()
|
void IInternalDisposableService.DisposeService()
|
||||||
|
|
@ -524,6 +550,18 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
||||||
this.ChatMessageUnhandled = null;
|
this.ChatMessageUnhandled = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public DalamudLinkPayload AddChatLinkHandler(Action<Guid, SeString> commandAction)
|
||||||
|
=> this.chatGuiService.AddChatLinkHandler(this.plugin.InternalName, commandAction);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveChatLinkHandler(Guid commandId)
|
||||||
|
=> this.chatGuiService.RemoveChatLinkHandler(this.plugin.InternalName, commandId);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void RemoveChatLinkHandler()
|
||||||
|
=> this.chatGuiService.RemoveChatLinkHandler(this.plugin.InternalName);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Print(XivChatEntry chat)
|
public void Print(XivChatEntry chat)
|
||||||
=> this.chatGuiService.Print(chat);
|
=> this.chatGuiService.Print(chat);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Threading;
|
||||||
|
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game.Addon.Events;
|
using Dalamud.Game.Addon.Events;
|
||||||
|
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
|
@ -330,7 +331,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
this.entriesReadOnlyCopy = null;
|
this.entriesReadOnlyCopy = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtkUnitBase* GetDtr() => (AtkUnitBase*)this.gameGui.GetAddonByName("_DTR").ToPointer();
|
private AtkUnitBase* GetDtr() => this.gameGui.GetAddonByName("_DTR").Struct;
|
||||||
|
|
||||||
private void Update(IFramework unused)
|
private void Update(IFramework unused)
|
||||||
{
|
{
|
||||||
|
|
@ -427,7 +428,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
|
|
||||||
private void FixCollision(AddonEvent eventType, AddonArgs addonInfo)
|
private void FixCollision(AddonEvent eventType, AddonArgs addonInfo)
|
||||||
{
|
{
|
||||||
var addon = (AtkUnitBase*)addonInfo.Addon;
|
var addon = addonInfo.Addon.Struct;
|
||||||
if (addon->RootNode is null || addon->UldManager.NodeList is null) return;
|
if (addon->RootNode is null || addon->UldManager.NodeList is null) return;
|
||||||
|
|
||||||
float minX = addon->RootNode->Width;
|
float minX = addon->RootNode->Width;
|
||||||
|
|
@ -596,24 +597,13 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
newTextNode->TextColor = new ByteColor { R = 255, G = 255, B = 255, A = 255 };
|
newTextNode->TextColor = new ByteColor { R = 255, G = 255, B = 255, A = 255 };
|
||||||
newTextNode->EdgeColor = new ByteColor { R = 142, G = 106, B = 12, A = 255 };
|
newTextNode->EdgeColor = new ByteColor { R = 142, G = 106, B = 12, A = 255 };
|
||||||
|
|
||||||
// ICreatable was restored, this may be necessary if AtkUldManager.CreateAtkTextNode(); is used instead of Create<T>
|
|
||||||
// // Memory is filled with random data after being created, zero out some things to avoid issues.
|
|
||||||
// newTextNode->UnkPtr_1 = null;
|
|
||||||
// newTextNode->SelectStart = 0;
|
|
||||||
// newTextNode->SelectEnd = 0;
|
|
||||||
// newTextNode->FontCacheHandle = 0;
|
|
||||||
// newTextNode->CharSpacing = 0;
|
|
||||||
// newTextNode->BackgroundColor = new ByteColor { R = 0, G = 0, B = 0, A = 0 };
|
|
||||||
// newTextNode->TextId = 0;
|
|
||||||
// newTextNode->SheetType = 0;
|
|
||||||
|
|
||||||
return newTextNode;
|
return newTextNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DtrEventHandler(AddonEventType atkEventType, IntPtr atkUnitBase, IntPtr atkResNode)
|
private void DtrEventHandler(AddonEventType atkEventType, AddonEventData eventData)
|
||||||
{
|
{
|
||||||
var addon = (AtkUnitBase*)atkUnitBase;
|
var addon = (AtkUnitBase*)eventData.AddonPointer;
|
||||||
var node = (AtkResNode*)atkResNode;
|
var node = (AtkResNode*)eventData.NodeTargetPointer;
|
||||||
|
|
||||||
DtrBarEntry? dtrBarEntry = null;
|
DtrBarEntry? dtrBarEntry = null;
|
||||||
this.entriesLock.EnterReadLock();
|
this.entriesLock.EnterReadLock();
|
||||||
|
|
@ -652,7 +642,7 @@ internal sealed unsafe class DtrBar : IInternalDisposableService, IDtrBar
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AddonEventType.MouseClick:
|
case AddonEventType.MouseClick:
|
||||||
dtrBarEntry.OnClick.Invoke();
|
dtrBarEntry.OnClick?.Invoke(new AddonMouseEventData(eventData));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
|
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Plugin.Internal.Types;
|
using Dalamud.Plugin.Internal.Types;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
@ -42,12 +43,6 @@ public interface IReadOnlyDtrBarEntry
|
||||||
/// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings.
|
/// Gets a value indicating whether the user has hidden this entry from view through the Dalamud settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UserHidden { get; }
|
public bool UserHidden { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Triggers the click action of this entry.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True, if a click action was registered and executed.</returns>
|
|
||||||
public bool TriggerClickAction();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -71,9 +66,9 @@ public interface IDtrBarEntry : IReadOnlyDtrBarEntry
|
||||||
public new bool Shown { get; set; }
|
public new bool Shown { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a action to be invoked when the user clicks on the dtr entry.
|
/// Gets or sets an action to be invoked when the user clicks on the dtr entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action? OnClick { get; set; }
|
public Action<AddonMouseEventData>? OnClick { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove this entry from the bar.
|
/// Remove this entry from the bar.
|
||||||
|
|
@ -122,10 +117,8 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
||||||
/// <inheritdoc cref="IDtrBarEntry.Tooltip" />
|
/// <inheritdoc cref="IDtrBarEntry.Tooltip" />
|
||||||
public SeString? Tooltip { get; set; }
|
public SeString? Tooltip { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets or sets a action to be invoked when the user clicks on the dtr entry.
|
public Action<AddonMouseEventData>? OnClick { get; set; }
|
||||||
/// </summary>
|
|
||||||
public Action? OnClick { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool HasClickAction => this.OnClick != null;
|
public bool HasClickAction => this.OnClick != null;
|
||||||
|
|
@ -145,7 +138,7 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
[Api13ToDo("Maybe make this config scoped to internalname?")]
|
[Api13ToDo("Maybe make this config scoped to internal name?")]
|
||||||
public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false;
|
public bool UserHidden => this.configuration.DtrIgnore?.Contains(this.Title) ?? false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -178,16 +171,6 @@ internal sealed unsafe class DtrBarEntry : IDisposable, IDtrBarEntry
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal LocalPlugin? OwnerPlugin { get; set; }
|
internal LocalPlugin? OwnerPlugin { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public bool TriggerClickAction()
|
|
||||||
{
|
|
||||||
if (this.OnClick == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
this.OnClick.Invoke();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove this entry from the bar.
|
/// Remove this entry from the bar.
|
||||||
/// You will need to re-acquire it from DtrBar to reuse it.
|
/// You will need to re-acquire it from DtrBar to reuse it.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
using Dalamud.Game.NativeWrapper;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
|
@ -165,79 +166,59 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr GetUIModule()
|
public UIModulePtr GetUIModule()
|
||||||
{
|
{
|
||||||
var framework = FFXIVClientStructs.FFXIV.Client.System.Framework.Framework.Instance();
|
return (nint)UIModule.Instance();
|
||||||
if (framework == null)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
var uiModule = framework->GetUIModule();
|
|
||||||
if (uiModule == null)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
return (IntPtr)uiModule;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr GetAddonByName(string name, int index = 1)
|
public AtkUnitBasePtr GetAddonByName(string name, int index = 1)
|
||||||
{
|
{
|
||||||
var atkStage = AtkStage.Instance();
|
var unitManager = RaptureAtkUnitManager.Instance();
|
||||||
if (atkStage == null)
|
if (unitManager == null)
|
||||||
return IntPtr.Zero;
|
return 0;
|
||||||
|
|
||||||
var unitMgr = atkStage->RaptureAtkUnitManager;
|
return (nint)unitManager->GetAddonByName(name, index);
|
||||||
if (unitMgr == null)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
var addon = unitMgr->GetAddonByName(name, index);
|
|
||||||
if (addon == null)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
return (IntPtr)addon;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr FindAgentInterface(string addonName)
|
public AgentInterfacePtr GetAgentById(int id)
|
||||||
|
{
|
||||||
|
var agentModule = AgentModule.Instance();
|
||||||
|
if (agentModule == null || id < 0 || id >= agentModule->Agents.Length)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (nint)agentModule->Agents[id].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AgentInterfacePtr FindAgentInterface(string addonName)
|
||||||
{
|
{
|
||||||
var addon = this.GetAddonByName(addonName);
|
var addon = this.GetAddonByName(addonName);
|
||||||
return this.FindAgentInterface(addon);
|
return this.FindAgentInterface(addon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr FindAgentInterface(void* addon) => this.FindAgentInterface((IntPtr)addon);
|
public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon)
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IntPtr FindAgentInterface(IntPtr addonPtr)
|
|
||||||
{
|
{
|
||||||
if (addonPtr == IntPtr.Zero)
|
if (addon.IsNull)
|
||||||
return IntPtr.Zero;
|
return 0;
|
||||||
|
|
||||||
var uiModule = (UIModule*)this.GetUIModule();
|
var agentModule = AgentModule.Instance();
|
||||||
if (uiModule == null)
|
|
||||||
return IntPtr.Zero;
|
|
||||||
|
|
||||||
var agentModule = uiModule->GetAgentModule();
|
|
||||||
if (agentModule == null)
|
if (agentModule == null)
|
||||||
return IntPtr.Zero;
|
return 0;
|
||||||
|
|
||||||
var addon = (AtkUnitBase*)addonPtr;
|
|
||||||
var addonId = addon->ParentId == 0 ? addon->Id : addon->ParentId;
|
|
||||||
|
|
||||||
|
var addonId = addon.ParentId == 0 ? addon.Id : addon.ParentId;
|
||||||
if (addonId == 0)
|
if (addonId == 0)
|
||||||
return IntPtr.Zero;
|
return 0;
|
||||||
|
|
||||||
var index = 0;
|
foreach (AgentInterface* agent in agentModule->Agents)
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
var agent = agentModule->GetAgentByInternalId((AgentId)index++);
|
if (agent != null && agent->AddonId == addonId)
|
||||||
if (agent == uiModule || agent == null)
|
return (nint)agent;
|
||||||
break;
|
|
||||||
|
|
||||||
if (agent->AddonId == addonId)
|
|
||||||
return new IntPtr(agent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return IntPtr.Zero;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -452,25 +433,25 @@ internal class GameGuiPluginScoped : IInternalDisposableService, IGameGui
|
||||||
=> this.gameGuiService.ScreenToWorld(screenPos, out worldPos, rayDistance);
|
=> this.gameGuiService.ScreenToWorld(screenPos, out worldPos, rayDistance);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr GetUIModule()
|
public UIModulePtr GetUIModule()
|
||||||
=> this.gameGuiService.GetUIModule();
|
=> this.gameGuiService.GetUIModule();
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr GetAddonByName(string name, int index = 1)
|
public AtkUnitBasePtr GetAddonByName(string name, int index = 1)
|
||||||
=> this.gameGuiService.GetAddonByName(name, index);
|
=> this.gameGuiService.GetAddonByName(name, index);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IntPtr FindAgentInterface(string addonName)
|
public AgentInterfacePtr GetAgentById(int id)
|
||||||
|
=> this.gameGuiService.GetAgentById(id);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AgentInterfacePtr FindAgentInterface(string addonName)
|
||||||
=> this.gameGuiService.FindAgentInterface(addonName);
|
=> this.gameGuiService.FindAgentInterface(addonName);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public unsafe IntPtr FindAgentInterface(void* addon)
|
public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon)
|
||||||
=> this.gameGuiService.FindAgentInterface(addon);
|
=> this.gameGuiService.FindAgentInterface(addon);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IntPtr FindAgentInterface(IntPtr addonPtr)
|
|
||||||
=> this.gameGuiService.FindAgentInterface(addonPtr);
|
|
||||||
|
|
||||||
private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled);
|
private void UiHideToggledForward(object sender, bool toggled) => this.UiHideToggled?.Invoke(sender, toggled);
|
||||||
|
|
||||||
private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId);
|
private void HoveredItemForward(object sender, ulong itemId) => this.HoveredItemChanged?.Invoke(sender, itemId);
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public unsafe void RequestRedraw()
|
public unsafe void RequestRedraw()
|
||||||
{
|
{
|
||||||
var addon = (AddonNamePlate*)this.gameGui.GetAddonByName("NamePlate");
|
var addon = (AddonNamePlate*)(nint)this.gameGui.GetAddonByName("NamePlate");
|
||||||
if (addon != null)
|
if (addon != null)
|
||||||
{
|
{
|
||||||
addon->DoFullUpdate = 1;
|
addon->DoFullUpdate = 1;
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ public enum JobFlags : ulong
|
||||||
RedMage = 1ul << 24,
|
RedMage = 1ul << 24,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Blue mage (BLM).
|
/// Blue mage (BLU).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BlueMage = 1ul << 25,
|
BlueMage = 1ul << 25,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
using Dalamud.Game.Command;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Dalamud.Hooking;
|
|
||||||
using Dalamud.Plugin.Services;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.Completion;
|
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
|
||||||
|
|
||||||
namespace Dalamud.Game.Internal;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This class adds dalamud and plugin commands to the chat box's autocompletion.
|
|
||||||
/// </summary>
|
|
||||||
[ServiceManager.EarlyLoadedService]
|
|
||||||
internal sealed unsafe class Completion : IInternalDisposableService
|
|
||||||
{
|
|
||||||
// 0xFF is a magic group number that causes CompletionModule's internals to treat entries
|
|
||||||
// as raw strings instead of as lookups into an EXD sheet
|
|
||||||
private const int GroupNumber = 0xFF;
|
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
|
||||||
private readonly CommandManager commandManager = Service<CommandManager>.Get();
|
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
|
||||||
private readonly Framework framework = Service<Framework>.Get();
|
|
||||||
|
|
||||||
private readonly Dictionary<string, EntryStrings> cachedCommands = [];
|
|
||||||
private readonly ConcurrentQueue<string> addedCommands = [];
|
|
||||||
|
|
||||||
private EntryStrings? dalamudCategory;
|
|
||||||
|
|
||||||
private Hook<CompletionModule.Delegates.GetSelection>? getSelection;
|
|
||||||
|
|
||||||
// This is marked volatile since we set and check it from different threads. Instead of using a synchronization
|
|
||||||
// primitive, a volatile is sufficient since the absolute worst case is that we delay one extra frame to reset
|
|
||||||
// the list, which is fine
|
|
||||||
private volatile bool needsClear;
|
|
||||||
private bool disposed;
|
|
||||||
private nint wantedVtblPtr;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Completion"/> class.
|
|
||||||
/// </summary>
|
|
||||||
[ServiceManager.ServiceConstructor]
|
|
||||||
internal Completion()
|
|
||||||
{
|
|
||||||
this.commandManager.CommandAdded += this.OnCommandAdded;
|
|
||||||
this.commandManager.CommandRemoved += this.OnCommandRemoved;
|
|
||||||
|
|
||||||
this.framework.Update += this.OnUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Finalizes an instance of the <see cref="Completion"/> class.</summary>
|
|
||||||
~Completion() => this.Dispose(false);
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
void IInternalDisposableService.DisposeService() => this.Dispose(true);
|
|
||||||
|
|
||||||
private static AtkUnitBase* FindOwningAddon(AtkComponentTextInput* component)
|
|
||||||
{
|
|
||||||
if (component == null) return null;
|
|
||||||
|
|
||||||
var node = (AtkResNode*)component->OwnerNode;
|
|
||||||
if (node == null) return null;
|
|
||||||
|
|
||||||
while (node->ParentNode != null)
|
|
||||||
node = node->ParentNode;
|
|
||||||
|
|
||||||
foreach (var addon in RaptureAtkUnitManager.Instance()->AllLoadedUnitsList.Entries)
|
|
||||||
{
|
|
||||||
if (addon.Value->RootNode == node)
|
|
||||||
return addon;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private AtkComponentTextInput* GetActiveTextInput()
|
|
||||||
{
|
|
||||||
var mod = RaptureAtkModule.Instance();
|
|
||||||
if (mod == null) return null;
|
|
||||||
|
|
||||||
var basePtr = mod->TextInput.TargetTextInputEventInterface;
|
|
||||||
if (basePtr == null) return null;
|
|
||||||
|
|
||||||
// Once CS has an implementation for multiple inheritance, we can remove this sig from dalamud
|
|
||||||
// as well as the nasty pointer arithmetic below. But for now, we need to do this manually.
|
|
||||||
// The AtkTextInputEventInterface* is the secondary base class for AtkComponentTextInput*
|
|
||||||
// so the pointer is sizeof(AtkComponentInputBase) into the object. We verify that we're looking
|
|
||||||
// at the object we think we are by confirming the pointed-to vtbl matches the known secondary vtbl for
|
|
||||||
// AtkComponentTextInput, and if it does, we can shift the pointer back to get the start of our text input
|
|
||||||
if (this.wantedVtblPtr == 0)
|
|
||||||
{
|
|
||||||
this.wantedVtblPtr =
|
|
||||||
Service<TargetSigScanner>.Get().GetStaticAddressFromSig(
|
|
||||||
"48 89 01 48 8D 05 ?? ?? ?? ?? 48 89 81 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 48 89 81 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 8B 48 68",
|
|
||||||
4);
|
|
||||||
}
|
|
||||||
|
|
||||||
var vtblPtr = *(nint*)basePtr;
|
|
||||||
if (vtblPtr != this.wantedVtblPtr) return null;
|
|
||||||
|
|
||||||
// This needs to be updated if the layout/base order of AtkComponentTextInput changes
|
|
||||||
return (AtkComponentTextInput*)((AtkComponentInputBase*)basePtr - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool AllowCompletion(string cmd)
|
|
||||||
{
|
|
||||||
// this is one of our commands, let's see if we should allow this to be completed
|
|
||||||
var component = this.GetActiveTextInput();
|
|
||||||
|
|
||||||
// ContainingAddon or ContainingAddon2 aren't always populated, but they
|
|
||||||
// seem to be in any case where this is actually a completable AtkComponentTextInput
|
|
||||||
// In the worst case, we can walk the AtkNode tree- but let's try the easy pointers first
|
|
||||||
var addon = component->ContainingAddon;
|
|
||||||
if (addon == null) addon = component->ContainingAddon2;
|
|
||||||
if (addon == null) addon = FindOwningAddon(component);
|
|
||||||
|
|
||||||
if (addon == null || addon->NameString != "ChatLog")
|
|
||||||
{
|
|
||||||
// we don't know what addon is completing, or we know it isn't ChatLog
|
|
||||||
// either way, we should just reject this completion
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're in ChatLog, so check if this is the start of the text input
|
|
||||||
// AtkComponentTextInput->UnkText1 is the evaluated version of the current text
|
|
||||||
// so if the command starts with that, then either it's empty or a prefix completion.
|
|
||||||
// In either case, we're happy to allow completion.
|
|
||||||
return cmd.StartsWith(component->UnkText1.StringPtr.ExtractText());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (this.disposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
this.getSelection?.Disable();
|
|
||||||
this.getSelection?.Dispose();
|
|
||||||
this.framework.Update -= this.OnUpdate;
|
|
||||||
this.commandManager.CommandAdded -= this.OnCommandAdded;
|
|
||||||
this.commandManager.CommandRemoved -= this.OnCommandRemoved;
|
|
||||||
|
|
||||||
this.dalamudCategory?.Dispose();
|
|
||||||
this.ClearCachedCommands();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCommandAdded(object? sender, CommandManager.CommandEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.CommandInfo.ShowInHelp)
|
|
||||||
this.addedCommands.Enqueue(e.Command);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCommandRemoved(object? sender, CommandManager.CommandEventArgs e) => this.needsClear = true;
|
|
||||||
|
|
||||||
private void OnUpdate(IFramework fw)
|
|
||||||
{
|
|
||||||
var atkModule = RaptureAtkModule.Instance();
|
|
||||||
if (atkModule == null) return;
|
|
||||||
|
|
||||||
var textInput = &atkModule->TextInput;
|
|
||||||
|
|
||||||
if (textInput->CompletionModule == null) return;
|
|
||||||
|
|
||||||
// Before we change _anything_ we need to check the state of the UI- if the completion list is open
|
|
||||||
// changes to the underlying data are extremely unsafe, so we'll just wait until the next frame
|
|
||||||
// worst case, someone tries to complete a command that _just_ got unloaded so it won't do anything
|
|
||||||
// but that's the same as making a typo, really
|
|
||||||
if (textInput->CompletionDepth > 0) return;
|
|
||||||
|
|
||||||
// Create the category for Dalamud commands.
|
|
||||||
// This needs to be done here, since we cannot create Utf8Strings before the game
|
|
||||||
// has initialized (no allocator set up yet).
|
|
||||||
this.dalamudCategory ??= new EntryStrings("【Dalamud】");
|
|
||||||
|
|
||||||
this.LoadCommands(textInput->CompletionModule);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CategoryData* EnsureCategoryData(CompletionModule* module)
|
|
||||||
{
|
|
||||||
if (module == null) return null;
|
|
||||||
|
|
||||||
if (this.getSelection == null)
|
|
||||||
{
|
|
||||||
this.getSelection = Hook<CompletionModule.Delegates.GetSelection>.FromAddress(
|
|
||||||
(IntPtr)module->VirtualTable->GetSelection,
|
|
||||||
this.GetSelectionDetour);
|
|
||||||
this.getSelection.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < module->CategoryNames.Count; i++)
|
|
||||||
{
|
|
||||||
if (module->CategoryNames[i].AsReadOnlySeStringSpan().ContainsText("【Dalamud】"u8))
|
|
||||||
{
|
|
||||||
return module->CategoryData[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the category since we don't have one
|
|
||||||
var categoryData = (CategoryData*)Memory.MemoryHelper.GameAllocateDefault((ulong)sizeof(CategoryData));
|
|
||||||
categoryData->Ctor(GroupNumber, 0xFF);
|
|
||||||
module->AddCategoryData(GroupNumber, this.dalamudCategory!.Display->StringPtr,
|
|
||||||
this.dalamudCategory.Match->StringPtr, categoryData);
|
|
||||||
|
|
||||||
return categoryData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearCachedCommands()
|
|
||||||
{
|
|
||||||
if (this.cachedCommands.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var entry in this.cachedCommands.Values)
|
|
||||||
{
|
|
||||||
entry.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cachedCommands.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadCommands(CompletionModule* completionModule)
|
|
||||||
{
|
|
||||||
if (completionModule == null) return;
|
|
||||||
if (completionModule->CategoryNames.Count == 0) return; // We want this data populated first
|
|
||||||
|
|
||||||
if (this.needsClear && this.cachedCommands.Count > 0)
|
|
||||||
{
|
|
||||||
this.needsClear = false;
|
|
||||||
completionModule->ClearCompletionData();
|
|
||||||
this.ClearCachedCommands();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var catData = this.EnsureCategoryData(completionModule);
|
|
||||||
if (catData == null) return;
|
|
||||||
|
|
||||||
if (catData->CompletionData.Count == 0)
|
|
||||||
{
|
|
||||||
var inputCommands = this.commandManager.Commands.Where(pair => pair.Value.ShowInHelp);
|
|
||||||
foreach (var (cmd, _) in inputCommands)
|
|
||||||
AddEntry(cmd);
|
|
||||||
catData->SortEntries();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var needsSort = false;
|
|
||||||
while (this.addedCommands.TryDequeue(out var cmd))
|
|
||||||
{
|
|
||||||
needsSort = true;
|
|
||||||
AddEntry(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsSort)
|
|
||||||
catData->SortEntries();
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
void AddEntry(string cmd)
|
|
||||||
{
|
|
||||||
if (this.cachedCommands.ContainsKey(cmd)) return;
|
|
||||||
|
|
||||||
var cmdStr = new EntryStrings(cmd);
|
|
||||||
this.cachedCommands.Add(cmd, cmdStr);
|
|
||||||
completionModule->AddCompletionEntry(
|
|
||||||
GroupNumber,
|
|
||||||
0xFF,
|
|
||||||
cmdStr.Display->StringPtr,
|
|
||||||
cmdStr.Match->StringPtr,
|
|
||||||
0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetSelectionDetour(CompletionModule* thisPtr, CategoryData.CompletionDataStruct* dataStructs, int index, Utf8String* outputString, Utf8String* outputDisplayString)
|
|
||||||
{
|
|
||||||
var ret = this.getSelection!.Original.Invoke(thisPtr, dataStructs, index, outputString, outputDisplayString);
|
|
||||||
if (ret != -2 || outputString == null) return ret;
|
|
||||||
|
|
||||||
// -2 means it was a plain text final selection, so it might be ours
|
|
||||||
// Unfortunately, the code that uses this string mangles the color macro for some reason...
|
|
||||||
// We'll just strip those out since we don't need the color in the chatbox
|
|
||||||
var txt = outputString->StringPtr.ExtractText();
|
|
||||||
if (!this.cachedCommands.ContainsKey(txt))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (!this.AllowCompletion(txt))
|
|
||||||
{
|
|
||||||
outputString->Clear();
|
|
||||||
if (outputDisplayString != null) outputDisplayString->Clear();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputString->SetString(txt + " ");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EntryStrings(string command) : IDisposable
|
|
||||||
{
|
|
||||||
public Utf8String* Display { get; } =
|
|
||||||
Utf8String.FromSequence(new SeStringBuilder().AddUiForeground(command, 539).BuiltString.EncodeWithNullTerminator());
|
|
||||||
|
|
||||||
public Utf8String* Match { get; } = Utf8String.FromString(command);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
this.Display->Dtor(true);
|
|
||||||
this.Match->Dtor(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
279
Dalamud/Game/Internal/DalamudCompletion.cs
Normal file
279
Dalamud/Game/Internal/DalamudCompletion.cs
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using Dalamud.Game.Command;
|
||||||
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.Completion;
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
using Lumina.Text;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class adds Dalamud and plugin commands to the chat box's autocompletion.
|
||||||
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
|
internal sealed unsafe class DalamudCompletion : IInternalDisposableService
|
||||||
|
{
|
||||||
|
// 0xFF is a magic group number that causes CompletionModule's internals to treat entries
|
||||||
|
// as raw strings instead of as lookups into an EXD sheet
|
||||||
|
private const int GroupNumber = 0xFF;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly CommandManager commandManager = Service<CommandManager>.Get();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly Framework framework = Service<Framework>.Get();
|
||||||
|
|
||||||
|
private readonly Dictionary<string, EntryStrings> cachedCommands = [];
|
||||||
|
|
||||||
|
private EntryStrings? dalamudCategory;
|
||||||
|
|
||||||
|
private Hook<AtkTextInput.Delegates.OpenCompletion> openSuggestionsHook;
|
||||||
|
private Hook<CompletionModule.Delegates.GetSelection>? getSelectionHook;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="DalamudCompletion"/> class.
|
||||||
|
/// </summary>
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
internal DalamudCompletion()
|
||||||
|
{
|
||||||
|
this.framework.RunOnTick(this.Setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
void IInternalDisposableService.DisposeService()
|
||||||
|
{
|
||||||
|
this.openSuggestionsHook?.Disable();
|
||||||
|
this.openSuggestionsHook?.Dispose();
|
||||||
|
|
||||||
|
this.getSelectionHook?.Disable();
|
||||||
|
this.getSelectionHook?.Dispose();
|
||||||
|
|
||||||
|
this.dalamudCategory?.Dispose();
|
||||||
|
|
||||||
|
this.ClearCachedCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Setup()
|
||||||
|
{
|
||||||
|
var uiModule = UIModule.Instance();
|
||||||
|
if (uiModule == null || uiModule->FrameCount == 0)
|
||||||
|
{
|
||||||
|
this.framework.RunOnTick(this.Setup);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dalamudCategory = new EntryStrings("【Dalamud】");
|
||||||
|
|
||||||
|
this.openSuggestionsHook = Hook<AtkTextInput.Delegates.OpenCompletion>.FromAddress(
|
||||||
|
(nint)AtkTextInput.MemberFunctionPointers.OpenCompletion,
|
||||||
|
this.OpenSuggestionsDetour);
|
||||||
|
|
||||||
|
this.getSelectionHook = Hook<CompletionModule.Delegates.GetSelection>.FromAddress(
|
||||||
|
(nint)uiModule->CompletionModule.VirtualTable->GetSelection,
|
||||||
|
this.GetSelectionDetour);
|
||||||
|
|
||||||
|
this.openSuggestionsHook.Enable();
|
||||||
|
this.getSelectionHook.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OpenSuggestionsDetour(AtkTextInput* thisPtr)
|
||||||
|
{
|
||||||
|
this.UpdateCompletionData();
|
||||||
|
this.openSuggestionsHook!.Original(thisPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetSelectionDetour(CompletionModule* thisPtr, CategoryData.CompletionDataStruct* dataStructs, int index, Utf8String* outputString, Utf8String* outputDisplayString)
|
||||||
|
{
|
||||||
|
var ret = this.getSelectionHook!.Original.Invoke(thisPtr, dataStructs, index, outputString, outputDisplayString);
|
||||||
|
this.HandleInsert(ret, outputString, outputDisplayString);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCompletionData()
|
||||||
|
{
|
||||||
|
if (!this.TryGetActiveTextInput(out var component, out var addon))
|
||||||
|
{
|
||||||
|
if (this.HasDalamudCategory())
|
||||||
|
this.ResetCompletionData();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uiModule = UIModule.Instance();
|
||||||
|
if (uiModule == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ResetCompletionData();
|
||||||
|
this.ClearCachedCommands();
|
||||||
|
|
||||||
|
var currentText = component->UnkText1.StringPtr.ExtractText();
|
||||||
|
|
||||||
|
var commands = this.commandManager.Commands
|
||||||
|
.Where(kv => kv.Value.ShowInHelp && (currentText.Length == 0 || kv.Key.StartsWith(currentText)))
|
||||||
|
.OrderBy(kv => kv.Key);
|
||||||
|
|
||||||
|
if (!commands.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var categoryData = (CategoryData*)IMemorySpace.GetDefaultSpace()->Malloc((ulong)sizeof(CategoryData), 0x08);
|
||||||
|
categoryData->Ctor(GroupNumber, 0xFF);
|
||||||
|
|
||||||
|
uiModule->CompletionModule.AddCategoryData(
|
||||||
|
GroupNumber,
|
||||||
|
this.dalamudCategory!.Display->StringPtr,
|
||||||
|
this.dalamudCategory.Match->StringPtr, categoryData);
|
||||||
|
|
||||||
|
foreach (var (cmd, info) in commands)
|
||||||
|
{
|
||||||
|
if (!this.cachedCommands.TryGetValue(cmd, out var entryString))
|
||||||
|
this.cachedCommands.Add(cmd, entryString = new EntryStrings(cmd));
|
||||||
|
|
||||||
|
uiModule->CompletionModule.AddCompletionEntry(
|
||||||
|
GroupNumber,
|
||||||
|
0xFF,
|
||||||
|
entryString.Display->StringPtr,
|
||||||
|
entryString.Match->StringPtr,
|
||||||
|
0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryData->SortEntries();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleInsert(int ret, Utf8String* outputString, Utf8String* outputDisplayString)
|
||||||
|
{
|
||||||
|
// -2 means it was a plain text final selection, so it might be ours.
|
||||||
|
if (ret != -2 || outputString == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Strip out color payloads that we added to the string.
|
||||||
|
var txt = outputString->StringPtr.ExtractText();
|
||||||
|
if (!this.cachedCommands.ContainsKey(txt))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!this.TryGetActiveTextInput(out _, out _))
|
||||||
|
{
|
||||||
|
outputString->Clear();
|
||||||
|
|
||||||
|
if (outputDisplayString != null)
|
||||||
|
outputDisplayString->Clear();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputString->SetString(txt + ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetActiveTextInput(out AtkComponentTextInput* component, out AtkUnitBase* addon)
|
||||||
|
{
|
||||||
|
component = null;
|
||||||
|
addon = null;
|
||||||
|
|
||||||
|
var raptureAtkModule = RaptureAtkModule.Instance();
|
||||||
|
if (raptureAtkModule == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var textInputEventInterface = raptureAtkModule->TextInput.TargetTextInputEventInterface;
|
||||||
|
if (textInputEventInterface == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ownerNode = textInputEventInterface->GetOwnerNode();
|
||||||
|
if (ownerNode == null || ownerNode->GetNodeType() != NodeType.Component)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var componentNode = (AtkComponentNode*)ownerNode;
|
||||||
|
var componentBase = componentNode->Component;
|
||||||
|
if (componentBase == null || componentBase->GetComponentType() != ComponentType.TextInput)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
component = (AtkComponentTextInput*)componentBase;
|
||||||
|
|
||||||
|
addon = component->ContainingAddon;
|
||||||
|
|
||||||
|
if (addon == null)
|
||||||
|
addon = component->ContainingAddon2;
|
||||||
|
|
||||||
|
if (addon == null)
|
||||||
|
addon = RaptureAtkUnitManager.Instance()->GetAddonByNode((AtkResNode*)component->OwnerNode);
|
||||||
|
|
||||||
|
return addon != null && addon->NameString == "ChatLog";
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasDalamudCategory()
|
||||||
|
{
|
||||||
|
var uiModule = UIModule.Instance();
|
||||||
|
if (uiModule == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (var i = 0; i < uiModule->CompletionModule.CategoryNames.Count; i++)
|
||||||
|
{
|
||||||
|
if (uiModule->CompletionModule.CategoryNames[i].AsReadOnlySeStringSpan().ContainsText("【Dalamud】"u8))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetCompletionData()
|
||||||
|
{
|
||||||
|
var uiModule = UIModule.Instance();
|
||||||
|
if (uiModule == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uiModule->CompletionModule.ClearCompletionData();
|
||||||
|
|
||||||
|
// This happens in UIModule.Update. Just repeat it to fill CompletionData back up with defaults.
|
||||||
|
uiModule->CompletionModule.Update(
|
||||||
|
&uiModule->CompletionSheetName,
|
||||||
|
&uiModule->CompletionOpenIconMacro,
|
||||||
|
&uiModule->CompletionCloseIconMacro,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearCachedCommands()
|
||||||
|
{
|
||||||
|
foreach (var entry in this.cachedCommands.Values)
|
||||||
|
{
|
||||||
|
entry.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cachedCommands.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EntryStrings : IDisposable
|
||||||
|
{
|
||||||
|
public EntryStrings(string command)
|
||||||
|
{
|
||||||
|
var rssb = SeStringBuilder.SharedPool.Get();
|
||||||
|
|
||||||
|
this.Display = Utf8String.FromSequence(rssb
|
||||||
|
.PushColorType(539)
|
||||||
|
.Append(command)
|
||||||
|
.PopColorType()
|
||||||
|
.GetViewAsSpan());
|
||||||
|
|
||||||
|
SeStringBuilder.SharedPool.Return(rssb);
|
||||||
|
|
||||||
|
this.Match = Utf8String.FromString(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Utf8String* Display { get; }
|
||||||
|
|
||||||
|
public Utf8String* Match { get; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.Display->Dtor(true);
|
||||||
|
this.Match->Dtor(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
|
using Dalamud.Game.Inventory.Records;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
|
|
@ -160,6 +162,25 @@ public unsafe struct GameInventoryItem : IEquatable<GameInventoryItem>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<MateriaEntry> MateriaEntries
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (ItemUtil.IsEventItem(this.BaseItemId) || this.IsMateriaUsedForDate)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
var result = new List<MateriaEntry>();
|
||||||
|
for (byte i = 0; i < this.InternalItem.GetMateriaCount(); i++)
|
||||||
|
{
|
||||||
|
var entry = new MateriaEntry(this.InternalItem.GetMateriaId(i), this.InternalItem.GetMateriaGrade(i));
|
||||||
|
if (entry.IsValid())
|
||||||
|
result.Add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the address of native inventory item in the game.<br />
|
/// Gets the address of native inventory item in the game.<br />
|
||||||
/// Can be 0 if this instance of <see cref="GameInventoryItem"/> does not point to a valid set of container type and slot.<br />
|
/// Can be 0 if this instance of <see cref="GameInventoryItem"/> does not point to a valid set of container type and slot.<br />
|
||||||
|
|
|
||||||
42
Dalamud/Game/Inventory/Records/MateriaEntry.cs
Normal file
42
Dalamud/Game/Inventory/Records/MateriaEntry.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Dalamud.Data;
|
||||||
|
|
||||||
|
using Lumina.Excel;
|
||||||
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.Inventory.Records;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A record to hold easy information about a given piece of Materia.
|
||||||
|
/// </summary>
|
||||||
|
public record MateriaEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MateriaEntry"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeId">The ID of the materia.</param>
|
||||||
|
/// <param name="gradeValue">The grade of the materia.</param>
|
||||||
|
public MateriaEntry(ushort typeId, byte gradeValue)
|
||||||
|
{
|
||||||
|
this.Type = LuminaUtils.CreateRef<Materia>(typeId);
|
||||||
|
this.Grade = LuminaUtils.CreateRef<MateriaGrade>(gradeValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Lumina row for this Materia.
|
||||||
|
/// </summary>
|
||||||
|
public RowRef<Materia> Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Lumina row for this Materia's grade.
|
||||||
|
/// </summary>
|
||||||
|
public RowRef<MateriaGrade> Grade { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if this MateriaEntry is valid.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if valid, false otherwise.</returns>
|
||||||
|
internal bool IsValid()
|
||||||
|
{
|
||||||
|
return this.Type.IsValid && this.Grade.IsValid;
|
||||||
|
}
|
||||||
|
}
|
||||||
96
Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs
Normal file
96
Dalamud/Game/NativeWrapper/AgentInterfacePtr.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A readonly wrapper for AgentInterface.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">The address to the AgentInterface.</param>
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||||
|
public readonly unsafe struct AgentInterfacePtr(nint address) : IEquatable<AgentInterfacePtr>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address to the AgentInterface.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x00)]
|
||||||
|
public readonly nint Address = address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsNull => this.Address == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the agents addon is visible.
|
||||||
|
/// </summary>
|
||||||
|
public readonly AtkUnitBasePtr Addon
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.IsNull)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
|
||||||
|
if (raptureAtkUnitManager == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (nint)raptureAtkUnitManager->GetAddonById(this.AddonId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the agent is active.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ushort AddonId => (ushort)(this.IsNull ? 0 : this.Struct->GetAddonId());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the agent is active.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsAgentActive => !this.IsNull && this.Struct->IsAgentActive();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the agents addon is ready.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsAddonReady => !this.IsNull && this.Struct->IsAddonReady();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the agents addon is visible.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsAddonShown => !this.IsNull && this.Struct->IsAddonShown();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the AgentInterface*.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks> Internal use only. </remarks>
|
||||||
|
internal readonly AgentInterface* Struct => (AgentInterface*)this.Address;
|
||||||
|
|
||||||
|
public static implicit operator nint(AgentInterfacePtr wrapper) => wrapper.Address;
|
||||||
|
|
||||||
|
public static implicit operator AgentInterfacePtr(nint address) => new(address);
|
||||||
|
|
||||||
|
public static implicit operator AgentInterfacePtr(void* ptr) => new((nint)ptr);
|
||||||
|
|
||||||
|
public static bool operator ==(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address == right.Address;
|
||||||
|
|
||||||
|
public static bool operator !=(AgentInterfacePtr left, AgentInterfacePtr right) => left.Address != right.Address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the AtkUnitBase.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns> <c>true</c> when the addon was focused, <c>false</c> otherwise. </returns>
|
||||||
|
public readonly bool FocusAddon() => this.IsNull && this.Struct->FocusAddon();
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified AgentInterfacePtr is equal to the current AgentInterfacePtr.</summary>
|
||||||
|
/// <param name="other">The AgentInterfacePtr to compare with the current AgentInterfacePtr.</param>
|
||||||
|
/// <returns><c>true</c> if the specified AgentInterfacePtr is equal to the current AgentInterfacePtr; otherwise, <c>false</c>.</returns>
|
||||||
|
public readonly bool Equals(AgentInterfacePtr other) => this.Address == other.Address;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||||
|
public override readonly bool Equals(object obj) => obj is AgentInterfacePtr wrapper && this.Equals(wrapper);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||||
|
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||||
|
}
|
||||||
171
Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs
Normal file
171
Dalamud/Game/NativeWrapper/AtkUnitBasePtr.cs
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
using FFXIVClientStructs.Interop;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A readonly wrapper for AtkUnitBase.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">The address to the AtkUnitBase.</param>
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||||
|
public readonly unsafe struct AtkUnitBasePtr(nint address) : IEquatable<AtkUnitBasePtr>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address to the AtkUnitBase.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x00)]
|
||||||
|
public readonly nint Address = address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsNull => this.Address == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the OnSetup function has been called.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsReady => !this.IsNull && this.Struct->IsReady;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the AtkUnitBase is visible.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsVisible => !this.IsNull && this.Struct->IsVisible;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name.
|
||||||
|
/// </summary>
|
||||||
|
public readonly string Name => this.IsNull ? string.Empty : this.Struct->NameString;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the id.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ushort Id => this.IsNull ? (ushort)0 : this.Struct->Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent id.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ushort ParentId => this.IsNull ? (ushort)0 : this.Struct->ParentId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the host id.
|
||||||
|
/// </summary>
|
||||||
|
public readonly ushort HostId => this.IsNull ? (ushort)0 : this.Struct->HostId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scale.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Scale => this.IsNull ? 0f : this.Struct->Scale;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the x-position.
|
||||||
|
/// </summary>
|
||||||
|
public readonly short X => this.IsNull ? (short)0 : this.Struct->X;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the y-position.
|
||||||
|
/// </summary>
|
||||||
|
public readonly short Y => this.IsNull ? (short)0 : this.Struct->Y;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the width.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Width => this.IsNull ? 0f : this.Struct->GetScaledWidth(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the height.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float Height => this.IsNull ? 0f : this.Struct->GetScaledHeight(false);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scaled width.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float ScaledWidth => this.IsNull ? 0f : this.Struct->GetScaledWidth(true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scaled height.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float ScaledHeight => this.IsNull ? 0f : this.Struct->GetScaledHeight(true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2 Position => new(this.X, this.Y);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the size.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2 Size => new(this.Width, this.Height);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the scaled size.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Vector2 ScaledSize => new(this.ScaledWidth, this.ScaledHeight);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of <see cref="AtkValue"/> entries.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int AtkValuesCount => this.Struct->AtkValuesCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an enumerable collection of <see cref="AtkValuePtr"/> of the addons current AtkValues.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// An <see cref="IEnumerable{T}"/> of <see cref="AtkValuePtr"/> corresponding to the addons AtkValues.
|
||||||
|
/// </returns>
|
||||||
|
public IEnumerable<AtkValuePtr> AtkValues
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
for (var i = 0; i < this.AtkValuesCount; i++)
|
||||||
|
{
|
||||||
|
AtkValuePtr ptr;
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
ptr = new AtkValuePtr((nint)this.Struct->AtkValuesSpan.GetPointer(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the AtkUnitBase*.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks> Internal use only. </remarks>
|
||||||
|
internal readonly AtkUnitBase* Struct => (AtkUnitBase*)this.Address;
|
||||||
|
|
||||||
|
public static implicit operator nint(AtkUnitBasePtr wrapper) => wrapper.Address;
|
||||||
|
|
||||||
|
public static implicit operator AtkUnitBasePtr(nint address) => new(address);
|
||||||
|
|
||||||
|
public static implicit operator AtkUnitBasePtr(void* ptr) => new((nint)ptr);
|
||||||
|
|
||||||
|
public static bool operator ==(AtkUnitBasePtr left, AtkUnitBasePtr right) => left.Address == right.Address;
|
||||||
|
|
||||||
|
public static bool operator !=(AtkUnitBasePtr left, AtkUnitBasePtr right) => left.Address != right.Address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focuses the AtkUnitBase.
|
||||||
|
/// </summary>
|
||||||
|
public readonly void Focus()
|
||||||
|
{
|
||||||
|
if (!this.IsNull)
|
||||||
|
this.Struct->Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified AtkUnitBasePtr is equal to the current AtkUnitBasePtr.</summary>
|
||||||
|
/// <param name="other">The AtkUnitBasePtr to compare with the current AtkUnitBasePtr.</param>
|
||||||
|
/// <returns><c>true</c> if the specified AtkUnitBasePtr is equal to the current AtkUnitBasePtr; otherwise, <c>false</c>.</returns>
|
||||||
|
public readonly bool Equals(AtkUnitBasePtr other) => this.Address == other.Address;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||||
|
public override readonly bool Equals(object obj) => obj is AtkUnitBasePtr wrapper && this.Equals(wrapper);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||||
|
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||||
|
}
|
||||||
113
Dalamud/Game/NativeWrapper/AtkValuePtr.cs
Normal file
113
Dalamud/Game/NativeWrapper/AtkValuePtr.cs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A readonly wrapper for AtkValue.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">The address to the AtkValue.</param>
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||||
|
public readonly unsafe struct AtkValuePtr(nint address) : IEquatable<AtkValuePtr>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address to the AtkValue.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x00)]
|
||||||
|
public readonly nint Address = address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsNull => this.Address == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value type.
|
||||||
|
/// </summary>
|
||||||
|
public readonly AtkValueType ValueType => (AtkValueType)this.Struct->Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the AtkValue*.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks> Internal use only. </remarks>
|
||||||
|
internal readonly AtkValue* Struct => (AtkValue*)this.Address;
|
||||||
|
|
||||||
|
public static implicit operator nint(AtkValuePtr wrapper) => wrapper.Address;
|
||||||
|
|
||||||
|
public static implicit operator AtkValuePtr(nint address) => new(address);
|
||||||
|
|
||||||
|
public static implicit operator AtkValuePtr(void* ptr) => new((nint)ptr);
|
||||||
|
|
||||||
|
public static bool operator ==(AtkValuePtr left, AtkValuePtr right) => left.Address == right.Address;
|
||||||
|
|
||||||
|
public static bool operator !=(AtkValuePtr left, AtkValuePtr right) => left.Address != right.Address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of the underlying <see cref="AtkValue"/> as a boxed object, based on its <see cref="AtkValueType"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// The boxed value represented by this <see cref="AtkValuePtr"/>, or <c>null</c> if the value is null or undefined.
|
||||||
|
/// </returns>
|
||||||
|
/// <exception cref="NotImplementedException">
|
||||||
|
/// Thrown if the value type is not currently handled by this implementation.
|
||||||
|
/// </exception>
|
||||||
|
public unsafe object? GetValue()
|
||||||
|
{
|
||||||
|
if (this.Struct == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return this.ValueType switch
|
||||||
|
{
|
||||||
|
AtkValueType.Undefined or AtkValueType.Null => null,
|
||||||
|
AtkValueType.Bool => this.Struct->Bool,
|
||||||
|
AtkValueType.Int => this.Struct->Int,
|
||||||
|
AtkValueType.Int64 => this.Struct->Int64,
|
||||||
|
AtkValueType.UInt => this.Struct->UInt,
|
||||||
|
AtkValueType.UInt64 => this.Struct->UInt64,
|
||||||
|
AtkValueType.Float => this.Struct->Float,
|
||||||
|
AtkValueType.String or AtkValueType.String8 or AtkValueType.ManagedString => this.Struct->String.HasValue ? this.Struct->String.AsReadOnlySeString() : default,
|
||||||
|
AtkValueType.WideString => this.Struct->WideString == null ? string.Empty : new string(this.Struct->WideString),
|
||||||
|
AtkValueType.Pointer => (nint)this.Struct->Pointer,
|
||||||
|
_ => throw new NotImplementedException($"AtkValueType {this.ValueType} is currently not supported"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to retrieve the value as a strongly-typed object if the underlying type matches.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The expected value type to extract.</typeparam>
|
||||||
|
/// <param name="result">
|
||||||
|
/// When this method returns <c>true</c>, contains the extracted value of type <typeparamref name="T"/>.
|
||||||
|
/// Otherwise, contains the default value of type <typeparamref name="T"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// <c>true</c> if the value was successfully extracted and matched <typeparamref name="T"/>; otherwise, <c>false</c>.
|
||||||
|
/// </returns>
|
||||||
|
public unsafe bool TryGet<T>([NotNullWhen(true)] out T? result) where T : struct
|
||||||
|
{
|
||||||
|
object? value = this.GetValue();
|
||||||
|
if (value is T typed)
|
||||||
|
{
|
||||||
|
result = typed;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified AtkValuePtr is equal to the current AtkValuePtr.</summary>
|
||||||
|
/// <param name="other">The AtkValuePtr to compare with the current AtkValuePtr.</param>
|
||||||
|
/// <returns><c>true</c> if the specified AtkValuePtr is equal to the current AtkValuePtr; otherwise, <c>false</c>.</returns>
|
||||||
|
public readonly bool Equals(AtkValuePtr other) => this.Address == other.Address || this.Struct->EqualTo(other.Struct);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||||
|
public override readonly bool Equals(object obj) => obj is AtkValuePtr wrapper && this.Equals(wrapper);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||||
|
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||||
|
}
|
||||||
87
Dalamud/Game/NativeWrapper/AtkValueType.cs
Normal file
87
Dalamud/Game/NativeWrapper/AtkValueType.cs
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
namespace Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the data type of the AtkValue.
|
||||||
|
/// </summary>
|
||||||
|
public enum AtkValueType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The value is undefined or invalid.
|
||||||
|
/// </summary>
|
||||||
|
Undefined = 0,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is null.
|
||||||
|
/// </summary>
|
||||||
|
Null = 0x1,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a boolean.
|
||||||
|
/// </summary>
|
||||||
|
Bool = 0x2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a 32-bit signed integer.
|
||||||
|
/// </summary>
|
||||||
|
Int = 0x3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a 64-bit signed integer.
|
||||||
|
/// </summary>
|
||||||
|
Int64 = 0x4,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a 32-bit unsigned integer.
|
||||||
|
/// </summary>
|
||||||
|
UInt = 0x5,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a 64-bit unsigned integer.
|
||||||
|
/// </summary>
|
||||||
|
UInt64 = 0x6,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a 32-bit floating-point number.
|
||||||
|
/// </summary>
|
||||||
|
Float = 0x7,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value points to a null-terminated 8-bit character string (ASCII or UTF-8).
|
||||||
|
/// </summary>
|
||||||
|
String = 0x8,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value points to a null-terminated 16-bit character string (UTF-16 / wide string).
|
||||||
|
/// </summary>
|
||||||
|
WideString = 0x9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value points to a constant null-terminated 8-bit character string (const char*).
|
||||||
|
/// </summary>
|
||||||
|
String8 = 0xA,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a vector.
|
||||||
|
/// </summary>
|
||||||
|
Vector = 0xB,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a pointer.
|
||||||
|
/// </summary>
|
||||||
|
Pointer = 0xC,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is pointing to an array of AtkValue entries.
|
||||||
|
/// </summary>
|
||||||
|
AtkValues = 0xD,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a managed string. See <see cref="String"/>.
|
||||||
|
/// </summary>
|
||||||
|
ManagedString = 0x28,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value is a managed vector. See <see cref="Vector"/>.
|
||||||
|
/// </summary>
|
||||||
|
ManagedVector = 0x2B,
|
||||||
|
}
|
||||||
51
Dalamud/Game/NativeWrapper/UIModulePtr.cs
Normal file
51
Dalamud/Game/NativeWrapper/UIModulePtr.cs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A readonly wrapper for UIModule.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">The address to the UIModule.</param>
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 0x08)]
|
||||||
|
public readonly unsafe struct UIModulePtr(nint address) : IEquatable<UIModulePtr>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The address to the UIModule.
|
||||||
|
/// </summary>
|
||||||
|
[FieldOffset(0x00)]
|
||||||
|
public readonly nint Address = address;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the underlying pointer is a nullptr.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsNull => this.Address == 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the UIModule*.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks> Internal use only. </remarks>
|
||||||
|
internal readonly UIModule* Struct => (UIModule*)this.Address;
|
||||||
|
|
||||||
|
public static implicit operator nint(UIModulePtr wrapper) => wrapper.Address;
|
||||||
|
|
||||||
|
public static implicit operator UIModulePtr(nint address) => new(address);
|
||||||
|
|
||||||
|
public static implicit operator UIModulePtr(void* ptr) => new((nint)ptr);
|
||||||
|
|
||||||
|
public static bool operator ==(UIModulePtr left, UIModulePtr right) => left.Address == right.Address;
|
||||||
|
|
||||||
|
public static bool operator !=(UIModulePtr left, UIModulePtr right) => left.Address != right.Address;
|
||||||
|
|
||||||
|
/// <summary>Determines whether the specified UIModulePtr is equal to the current UIModulePtr.</summary>
|
||||||
|
/// <param name="other">The UIModulePtr to compare with the current UIModulePtr.</param>
|
||||||
|
/// <returns><c>true</c> if the specified UIModulePtr is equal to the current UIModulePtr; otherwise, <c>false</c>.</returns>
|
||||||
|
public readonly bool Equals(UIModulePtr other) => this.Address == other.Address;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.Equals(object?)"/>
|
||||||
|
public override readonly bool Equals(object obj) => obj is UIModulePtr wrapper && this.Equals(wrapper);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="object.GetHashCode()"/>
|
||||||
|
public override readonly int GetHashCode() => ((nuint)this.Address).GetHashCode();
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Dalamud.Game.Network;
|
||||||
/// This class handles interacting with game network events.
|
/// This class handles interacting with game network events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetwork
|
internal sealed unsafe class GameNetwork : IInternalDisposableService
|
||||||
{
|
{
|
||||||
private readonly GameNetworkAddressResolver address;
|
private readonly GameNetworkAddressResolver address;
|
||||||
private readonly Hook<PacketDispatcher.Delegates.OnReceivePacket> processZonePacketDownHook;
|
private readonly Hook<PacketDispatcher.Delegates.OnReceivePacket> processZonePacketDownHook;
|
||||||
|
|
@ -51,11 +51,23 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
||||||
this.processZonePacketUpHook.Enable();
|
this.processZonePacketUpHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delegate type of a network message event.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataPtr">The pointer to the raw data.</param>
|
||||||
|
/// <param name="opCode">The operation ID code.</param>
|
||||||
|
/// <param name="sourceActorId">The source actor ID.</param>
|
||||||
|
/// <param name="targetActorId">The taret actor ID.</param>
|
||||||
|
/// <param name="direction">The direction of the packed.</param>
|
||||||
|
public delegate void OnNetworkMessageDelegate(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
|
private delegate byte ProcessZonePacketUpDelegate(IntPtr a1, IntPtr dataPtr, IntPtr a3, byte a4);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <summary>
|
||||||
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
|
/// Event that is called when a network message is sent/received.
|
||||||
|
/// </summary>
|
||||||
|
public event OnNetworkMessageDelegate? NetworkMessage;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
void IInternalDisposableService.DisposeService()
|
void IInternalDisposableService.DisposeService()
|
||||||
|
|
@ -136,39 +148,3 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw
|
||||||
return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4);
|
return this.processZonePacketUpHook.Original(a1, dataPtr, a3, a4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Plugin-scoped version of a AddonLifecycle service.
|
|
||||||
/// </summary>
|
|
||||||
[PluginInterface]
|
|
||||||
[ServiceManager.ScopedService]
|
|
||||||
#pragma warning disable SA1015
|
|
||||||
[ResolveVia<IGameNetwork>]
|
|
||||||
#pragma warning restore SA1015
|
|
||||||
internal class GameNetworkPluginScoped : IInternalDisposableService, IGameNetwork
|
|
||||||
{
|
|
||||||
[ServiceManager.ServiceDependency]
|
|
||||||
private readonly GameNetwork gameNetworkService = Service<GameNetwork>.Get();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="GameNetworkPluginScoped"/> class.
|
|
||||||
/// </summary>
|
|
||||||
internal GameNetworkPluginScoped()
|
|
||||||
{
|
|
||||||
this.gameNetworkService.NetworkMessage += this.NetworkMessageForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
void IInternalDisposableService.DisposeService()
|
|
||||||
{
|
|
||||||
this.gameNetworkService.NetworkMessage -= this.NetworkMessageForward;
|
|
||||||
|
|
||||||
this.NetworkMessage = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NetworkMessageForward(nint dataPtr, ushort opCode, uint sourceActorId, uint targetActorId, NetworkMessageDirection direction)
|
|
||||||
=> this.NetworkMessage?.Invoke(dataPtr, opCode, sourceActorId, targetActorId, direction);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ using Dalamud.Utility;
|
||||||
|
|
||||||
using Lumina.Extensions;
|
using Lumina.Extensions;
|
||||||
|
|
||||||
using ItemKind = Dalamud.Game.Text.SeStringHandling.Payloads.ItemPayload.ItemKind;
|
|
||||||
using LSheets = Lumina.Excel.Sheets;
|
using LSheets = Lumina.Excel.Sheets;
|
||||||
|
|
||||||
namespace Dalamud.Game.Text.Evaluator.Internal;
|
namespace Dalamud.Game.Text.Evaluator.Internal;
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,6 @@ using StatusSheet = Lumina.Excel.Sheets.Status;
|
||||||
|
|
||||||
namespace Dalamud.Game.Text.Evaluator;
|
namespace Dalamud.Game.Text.Evaluator;
|
||||||
|
|
||||||
#pragma warning disable SeStringEvaluator
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluator for SeStrings.
|
/// Evaluator for SeStrings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ public class DalamudLinkPayload : Payload
|
||||||
public override PayloadType Type => PayloadType.DalamudLink;
|
public override PayloadType Type => PayloadType.DalamudLink;
|
||||||
|
|
||||||
/// <summary>Gets the plugin command ID to be linked.</summary>
|
/// <summary>Gets the plugin command ID to be linked.</summary>
|
||||||
public uint CommandId { get; internal set; }
|
public Guid CommandId { get; internal set; }
|
||||||
|
|
||||||
/// <summary>Gets an optional extra integer value 1.</summary>
|
/// <summary>Gets an optional extra integer value 1.</summary>
|
||||||
public int Extra1 { get; internal set; }
|
public int Extra1 { get; internal set; }
|
||||||
|
|
@ -40,7 +40,7 @@ public class DalamudLinkPayload : Payload
|
||||||
var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get();
|
var ssb = Lumina.Text.SeStringBuilder.SharedPool.Get();
|
||||||
var res = ssb.BeginMacro(MacroCode.Link)
|
var res = ssb.BeginMacro(MacroCode.Link)
|
||||||
.AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1)
|
.AppendIntExpression((int)EmbeddedInfoType.DalamudLink - 1)
|
||||||
.AppendUIntExpression(this.CommandId)
|
.AppendStringExpression(this.CommandId.ToString())
|
||||||
.AppendIntExpression(this.Extra1)
|
.AppendIntExpression(this.Extra1)
|
||||||
.AppendIntExpression(this.Extra2)
|
.AppendIntExpression(this.Extra2)
|
||||||
.BeginStringExpression()
|
.BeginStringExpression()
|
||||||
|
|
@ -72,15 +72,15 @@ public class DalamudLinkPayload : Payload
|
||||||
if (!pluginExpression.TryGetString(out var pluginString))
|
if (!pluginExpression.TryGetString(out var pluginString))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!commandIdExpression.TryGetUInt(out var commandId))
|
if (!commandIdExpression.TryGetString(out var commandId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.Plugin = pluginString.ExtractText();
|
this.Plugin = pluginString.ExtractText();
|
||||||
this.CommandId = commandId;
|
this.CommandId = Guid.Parse(commandId.ExtractText());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!commandIdExpression.TryGetUInt(out var commandId))
|
if (!commandIdExpression.TryGetString(out var commandId))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!extra1Expression.TryGetInt(out var extra1))
|
if (!extra1Expression.TryGetInt(out var extra1))
|
||||||
|
|
@ -102,7 +102,7 @@ public class DalamudLinkPayload : Payload
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.CommandId = commandId;
|
this.CommandId = Guid.Parse(commandId.ExtractText());
|
||||||
this.Extra1 = extra1;
|
this.Extra1 = extra1;
|
||||||
this.Extra2 = extra2;
|
this.Extra2 = extra2;
|
||||||
this.Plugin = extraData[0];
|
this.Plugin = extraData[0];
|
||||||
|
|
|
||||||
|
|
@ -72,33 +72,6 @@ public class ItemPayload : Payload
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Kinds of items that can be fetched from this payload.
|
|
||||||
/// </summary>
|
|
||||||
[Api13ToDo("Move this out of ItemPayload. It's used in other classes too.")]
|
|
||||||
public enum ItemKind : uint
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Normal items.
|
|
||||||
/// </summary>
|
|
||||||
Normal,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collectible Items.
|
|
||||||
/// </summary>
|
|
||||||
Collectible = 500_000,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// High-Quality items.
|
|
||||||
/// </summary>
|
|
||||||
Hq = 1_000_000,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Event/Key items.
|
|
||||||
/// </summary>
|
|
||||||
EventItem = 2_000_000,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override PayloadType Type => PayloadType.Item;
|
public override PayloadType Type => PayloadType.Item;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ public class SeString
|
||||||
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
||||||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||||
public static SeString CreateItemLink(uint itemId, bool isHq, string? displayNameOverride = null) =>
|
public static SeString CreateItemLink(uint itemId, bool isHq, string? displayNameOverride = null) =>
|
||||||
CreateItemLink(itemId, isHq ? ItemPayload.ItemKind.Hq : ItemPayload.ItemKind.Normal, displayNameOverride);
|
CreateItemLink(itemId, isHq ? ItemKind.Hq : ItemKind.Normal, displayNameOverride);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
|
/// Creates an SeString representing an entire Payload chain that can be used to link an item in the chat log.
|
||||||
|
|
@ -190,7 +190,7 @@ public class SeString
|
||||||
/// <param name="kind">The kind of item to link.</param>
|
/// <param name="kind">The kind of item to link.</param>
|
||||||
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
/// <param name="displayNameOverride">An optional name override to display, instead of the actual item name.</param>
|
||||||
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
/// <returns>An SeString containing all the payloads necessary to display an item link in the chat log.</returns>
|
||||||
public static SeString CreateItemLink(uint itemId, ItemPayload.ItemKind kind = ItemPayload.ItemKind.Normal, string? displayNameOverride = null)
|
public static SeString CreateItemLink(uint itemId, ItemKind kind = ItemKind.Normal, string? displayNameOverride = null)
|
||||||
{
|
{
|
||||||
var clientState = Service<ClientState.ClientState>.Get();
|
var clientState = Service<ClientState.ClientState>.Get();
|
||||||
var seStringEvaluator = Service<SeStringEvaluator>.Get();
|
var seStringEvaluator = Service<SeStringEvaluator>.Get();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Dalamud.Game.Text.SeStringHandling;
|
namespace Dalamud.Game.Text.SeStringHandling;
|
||||||
|
|
||||||
|
|
@ -126,7 +127,7 @@ public class SeStringBuilder
|
||||||
/// <param name="kind">Kind of item to encode.</param>
|
/// <param name="kind">Kind of item to encode.</param>
|
||||||
/// <param name="itemNameOverride">Override for the item's name.</param>
|
/// <param name="itemNameOverride">Override for the item's name.</param>
|
||||||
/// <returns>The current builder.</returns>
|
/// <returns>The current builder.</returns>
|
||||||
public SeStringBuilder AddItemLink(uint itemId, ItemPayload.ItemKind kind = ItemPayload.ItemKind.Normal, string? itemNameOverride = null) =>
|
public SeStringBuilder AddItemLink(uint itemId, ItemKind kind = ItemKind.Normal, string? itemNameOverride = null) =>
|
||||||
this.Append(SeString.CreateItemLink(itemId, kind, itemNameOverride));
|
this.Append(SeString.CreateItemLink(itemId, kind, itemNameOverride));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ internal unsafe class UiDebug
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
ImGuiHelpers.ClickToCopyText($"Address: {(ulong)atkUnitBase:X}", $"{(ulong)atkUnitBase:X}");
|
ImGuiHelpers.ClickToCopyText($"Address: {(nint)atkUnitBase:X}", $"{(nint)atkUnitBase:X}");
|
||||||
ImGuiHelpers.ClickToCopyText($"Agent: {(ulong)agent:X}", $"{(ulong)agent:X}");
|
ImGuiHelpers.ClickToCopyText($"Agent: {(nint)agent:X}", $"{(nint)agent:X}");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]");
|
ImGui.Text($"Position: [ {atkUnitBase->X} , {atkUnitBase->Y} ]");
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ public unsafe partial class AddonTree : IDisposable
|
||||||
{
|
{
|
||||||
var ptr = GameGui.GetAddonByName(name);
|
var ptr = GameGui.GetAddonByName(name);
|
||||||
|
|
||||||
if ((AtkUnitBase*)ptr != null)
|
if (!ptr.IsNull)
|
||||||
{
|
{
|
||||||
if (AddonTrees.TryGetValue(name, out var tree))
|
if (AddonTrees.TryGetValue(name, out var tree))
|
||||||
{
|
{
|
||||||
|
|
@ -151,7 +151,7 @@ public unsafe partial class AddonTree : IDisposable
|
||||||
var uldManager = addon->UldManager;
|
var uldManager = addon->UldManager;
|
||||||
|
|
||||||
PrintFieldValuePair("Address", $"{(nint)addon:X}");
|
PrintFieldValuePair("Address", $"{(nint)addon:X}");
|
||||||
PrintFieldValuePair("Agent", $"{GameGui.FindAgentInterface(addon):X}");
|
PrintFieldValuePair("Agent", $"{(nint)GameGui.FindAgentInterface(addon):X}");
|
||||||
|
|
||||||
PrintFieldValuePairs(
|
PrintFieldValuePairs(
|
||||||
("X", $"{addon->X}"),
|
("X", $"{addon->X}"),
|
||||||
|
|
@ -233,7 +233,7 @@ public unsafe partial class AddonTree : IDisposable
|
||||||
/// <returns>true if the addon is found.</returns>
|
/// <returns>true if the addon is found.</returns>
|
||||||
private bool ValidateAddon(out AtkUnitBase* addon)
|
private bool ValidateAddon(out AtkUnitBase* addon)
|
||||||
{
|
{
|
||||||
addon = (AtkUnitBase*)GameGui.GetAddonByName(this.AddonName);
|
addon = GameGui.GetAddonByName(this.AddonName).Struct;
|
||||||
if (addon == null || (nint)addon != this.InitialPtr)
|
if (addon == null || (nint)addon != this.InitialPtr)
|
||||||
{
|
{
|
||||||
this.Dispose();
|
this.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ internal unsafe partial class UiDebug2
|
||||||
/// Gets the base address for all unit lists.
|
/// Gets the base address for all unit lists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The address, if found.</returns>
|
/// <returns>The address, if found.</returns>
|
||||||
internal static AtkUnitList* GetUnitListBaseAddr() => &((UIModule*)GameGui.GetUIModule())->GetRaptureAtkModule()->RaptureAtkUnitManager.AtkUnitManager.DepthLayerOneList;
|
internal static AtkUnitList* GetUnitListBaseAddr() => &RaptureAtkUnitManager.Instance()->DepthLayerOneList;
|
||||||
|
|
||||||
private void DrawSidebar()
|
private void DrawSidebar()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.Gui;
|
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Game.NativeWrapper;
|
||||||
|
|
||||||
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
|
||||||
|
|
||||||
|
|
@ -12,7 +11,7 @@ internal unsafe class AddonWidget : IDataWindowWidget
|
||||||
{
|
{
|
||||||
private string inputAddonName = string.Empty;
|
private string inputAddonName = string.Empty;
|
||||||
private int inputAddonIndex;
|
private int inputAddonIndex;
|
||||||
private nint findAgentInterfacePtr;
|
private AgentInterfacePtr agentInterfacePtr;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public string DisplayName { get; init; } = "Addon";
|
public string DisplayName { get; init; } = "Addon";
|
||||||
|
|
@ -40,30 +39,27 @@ internal unsafe class AddonWidget : IDataWindowWidget
|
||||||
if (this.inputAddonName.IsNullOrEmpty())
|
if (this.inputAddonName.IsNullOrEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var address = gameGui.GetAddonByName(this.inputAddonName, this.inputAddonIndex);
|
var addon = gameGui.GetAddonByName(this.inputAddonName, this.inputAddonIndex);
|
||||||
|
if (addon.IsNull)
|
||||||
if (address == nint.Zero)
|
|
||||||
{
|
{
|
||||||
ImGui.Text("Null");
|
ImGui.Text("Null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
|
ImGui.TextUnformatted($"{addon.Name} - {Util.DescribeAddress(addon)}\n v:{addon.IsVisible} x:{addon.X} y:{addon.Y} s:{addon.Scale}, w:{addon.Width}, h:{addon.Height}");
|
||||||
var name = addon->NameString;
|
|
||||||
ImGui.TextUnformatted($"{name} - {Util.DescribeAddress(address)}\n v:{addon->IsVisible} x:{addon->X} y:{addon->Y} s:{addon->Scale}, w:{addon->RootNode->Width}, h:{addon->RootNode->Height}");
|
|
||||||
|
|
||||||
if (ImGui.Button("Find Agent"))
|
if (ImGui.Button("Find Agent"))
|
||||||
{
|
{
|
||||||
this.findAgentInterfacePtr = gameGui.FindAgentInterface(address);
|
this.agentInterfacePtr = gameGui.FindAgentInterface(addon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.findAgentInterfacePtr != nint.Zero)
|
if (!this.agentInterfacePtr.IsNull)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted($"Agent: {Util.DescribeAddress(this.findAgentInterfacePtr)}");
|
ImGui.TextUnformatted($"Agent: {Util.DescribeAddress(this.agentInterfacePtr)}");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGui.Button("C"))
|
if (ImGui.Button("C"))
|
||||||
ImGui.SetClipboardText(this.findAgentInterfacePtr.ToInt64().ToString("X"));
|
ImGui.SetClipboardText(this.agentInterfacePtr.Address.ToString("X"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps;
|
namespace Dalamud.Interface.Internal.Windows.SelfTest.Steps;
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ internal class ItemPayloadSelfTestStep : ISelfTestStep
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.PrintHqItem:
|
case SubStep.PrintHqItem:
|
||||||
toPrint = SeString.CreateItemLink(hqItemId, ItemPayload.ItemKind.Hq);
|
toPrint = SeString.CreateItemLink(hqItemId, ItemKind.Hq);
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.HoverHqItem:
|
case SubStep.HoverHqItem:
|
||||||
|
|
@ -68,7 +68,7 @@ internal class ItemPayloadSelfTestStep : ISelfTestStep
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.PrintCollectable:
|
case SubStep.PrintCollectable:
|
||||||
toPrint = SeString.CreateItemLink(collectableItemId, ItemPayload.ItemKind.Collectible);
|
toPrint = SeString.CreateItemLink(collectableItemId, ItemKind.Collectible);
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.HoverCollectable:
|
case SubStep.HoverCollectable:
|
||||||
|
|
@ -78,7 +78,7 @@ internal class ItemPayloadSelfTestStep : ISelfTestStep
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.PrintEventItem:
|
case SubStep.PrintEventItem:
|
||||||
toPrint = SeString.CreateItemLink(eventItemId, ItemPayload.ItemKind.EventItem);
|
toPrint = SeString.CreateItemLink(eventItemId, ItemKind.EventItem);
|
||||||
this.currentSubStep++;
|
this.currentSubStep++;
|
||||||
break;
|
break;
|
||||||
case SubStep.HoverEventItem:
|
case SubStep.HoverEventItem:
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ internal class MarketBoardSelfTestStep : ISelfTestStep
|
||||||
ImGui.Text($"Quantity: {this.historyListing.Quantity.ToString()}");
|
ImGui.Text($"Quantity: {this.historyListing.Quantity.ToString()}");
|
||||||
ImGui.Text($"Buyer: {this.historyListing.BuyerName}");
|
ImGui.Text($"Buyer: {this.historyListing.BuyerName}");
|
||||||
ImGui.Text($"Sale Price: {this.historyListing.SalePrice.ToString()}");
|
ImGui.Text($"Sale Price: {this.historyListing.SalePrice.ToString()}");
|
||||||
ImGui.Text($"Purchase Time: {this.historyListing.PurchaseTime.ToString(CultureInfo.InvariantCulture)}");
|
ImGui.Text($"Purchase Time: {this.historyListing.PurchaseTime.ToLocalTime().ToString(CultureInfo.InvariantCulture)}");
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
if (ImGui.Button("Looks Correct / Skip"))
|
if (ImGui.Button("Looks Correct / Skip"))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -473,7 +473,7 @@ internal class TitleScreenMenuWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
if (args is not AddonDrawArgs drawArgs) return;
|
if (args is not AddonDrawArgs drawArgs) return;
|
||||||
|
|
||||||
var addon = (AtkUnitBase*)drawArgs.Addon;
|
var addon = drawArgs.Addon.Struct;
|
||||||
var textNode = addon->GetTextNodeById(3);
|
var textNode = addon->GetTextNodeById(3);
|
||||||
|
|
||||||
// look and feel init. should be harmless to set.
|
// look and feel init. should be harmless to set.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -116,19 +116,11 @@ internal sealed partial class FontAtlasFactory
|
||||||
public IFontSpec DefaultFontSpec =>
|
public IFontSpec DefaultFontSpec =>
|
||||||
this.DefaultFontSpecOverride
|
this.DefaultFontSpecOverride
|
||||||
?? Service<DalamudConfiguration>.Get().DefaultFontSpec
|
?? Service<DalamudConfiguration>.Get().DefaultFontSpec
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
?? new SingleFontSpec()
|
||||||
?? (Service<DalamudConfiguration>.Get().UseAxisFontsFromGame
|
{
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
FontId = new GameFontAndFamilyId(GameFontFamily.Axis),
|
||||||
? new()
|
SizePx = InterfaceManager.DefaultFontSizePx,
|
||||||
{
|
};
|
||||||
FontId = new GameFontAndFamilyId(GameFontFamily.Axis),
|
|
||||||
SizePx = InterfaceManager.DefaultFontSizePx,
|
|
||||||
}
|
|
||||||
: new SingleFontSpec
|
|
||||||
{
|
|
||||||
FontId = new DalamudAssetFontAndFamilyId(DalamudAsset.NotoSansJpMedium),
|
|
||||||
SizePx = InterfaceManager.DefaultFontSizePx + 1,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the service instance of <see cref="Framework"/>.
|
/// Gets the service instance of <see cref="Framework"/>.
|
||||||
|
|
|
||||||
31
Dalamud/Plugin/ActivePluginsChangedEventArgs.cs
Normal file
31
Dalamud/Plugin/ActivePluginsChangedEventArgs.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Dalamud.Plugin;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains data about changes to the list of active plugins.
|
||||||
|
/// </summary>
|
||||||
|
public class ActivePluginsChangedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ActivePluginsChangedEventArgs"/> class
|
||||||
|
/// with the specified parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The kind of change that triggered the event.</param>
|
||||||
|
/// <param name="affectedInternalNames">The internal names of the plugins affected by the change.</param>
|
||||||
|
internal ActivePluginsChangedEventArgs(PluginListInvalidationKind kind, IEnumerable<string> affectedInternalNames)
|
||||||
|
{
|
||||||
|
this.Kind = kind;
|
||||||
|
this.AffectedInternalNames = affectedInternalNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the invalidation kind that caused this event to be fired.
|
||||||
|
/// </summary>
|
||||||
|
public PluginListInvalidationKind Kind { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the InternalNames of affected plugins.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> AffectedInternalNames { get; }
|
||||||
|
}
|
||||||
|
|
@ -13,8 +13,6 @@ using Dalamud.Data;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.Sanitizer;
|
using Dalamud.Game.Text.Sanitizer;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
using Dalamud.Interface.Internal.Windows.PluginInstaller;
|
||||||
|
|
@ -427,39 +425,6 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Chat Links
|
|
||||||
|
|
||||||
// TODO API9: Move to chatgui, don't allow passing own commandId
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a chat link handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandId">The ID of the command.</param>
|
|
||||||
/// <param name="commandAction">The action to be executed.</param>
|
|
||||||
/// <returns>Returns an SeString payload for the link.</returns>
|
|
||||||
public DalamudLinkPayload AddChatLinkHandler(uint commandId, Action<uint, SeString> commandAction)
|
|
||||||
{
|
|
||||||
return Service<ChatGui>.Get().AddChatLinkHandler(this.plugin.InternalName, commandId, commandAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove a chat link handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandId">The ID of the command.</param>
|
|
||||||
public void RemoveChatLinkHandler(uint commandId)
|
|
||||||
{
|
|
||||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.plugin.InternalName, commandId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all chat link handlers registered by the plugin.
|
|
||||||
/// </summary>
|
|
||||||
public void RemoveChatLinkHandler()
|
|
||||||
{
|
|
||||||
Service<ChatGui>.Get().RemoveChatLinkHandler(this.plugin.InternalName);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Dependency Injection
|
#region Dependency Injection
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
@ -523,15 +488,14 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dispatch the active plugins changed event.
|
/// Dispatch the active plugins changed event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kind">What action caused this event to be fired.</param>
|
/// <param name="args">The event arguments containing information about the change.</param>
|
||||||
/// <param name="affectedThisPlugin">If this plugin was affected by the change.</param>
|
internal void NotifyActivePluginsChanged(ActivePluginsChangedEventArgs args)
|
||||||
internal void NotifyActivePluginsChanged(PluginListInvalidationKind kind, bool affectedThisPlugin)
|
|
||||||
{
|
{
|
||||||
foreach (var action in Delegate.EnumerateInvocationList(this.ActivePluginsChanged))
|
foreach (var action in Delegate.EnumerateInvocationList(this.ActivePluginsChanged))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action(kind, affectedThisPlugin);
|
action(args);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -32,9 +32,8 @@ public interface IDalamudPluginInterface
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for events that listen to changes to the list of active plugins.
|
/// Delegate for events that listen to changes to the list of active plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kind">What action caused this event to be fired.</param>
|
/// <param name="args">The event arguments containing information about the change.</param>
|
||||||
/// <param name="affectedThisPlugin">If this plugin was affected by the change.</param>
|
public delegate void ActivePluginsChangedDelegate(ActivePluginsChangedEventArgs args);
|
||||||
public delegate void ActivePluginsChangedDelegate(PluginListInvalidationKind kind, bool affectedThisPlugin);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that gets fired when loc is changed
|
/// Event that gets fired when loc is changed
|
||||||
|
|
@ -281,25 +280,6 @@ public interface IDalamudPluginInterface
|
||||||
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc.</returns>
|
/// <returns>directory with path of AppData/XIVLauncher/pluginConfig/PluginInternalName/loc.</returns>
|
||||||
string GetPluginLocDirectory();
|
string GetPluginLocDirectory();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a chat link handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandId">The ID of the command.</param>
|
|
||||||
/// <param name="commandAction">The action to be executed.</param>
|
|
||||||
/// <returns>Returns an SeString payload for the link.</returns>
|
|
||||||
DalamudLinkPayload AddChatLinkHandler(uint commandId, Action<uint, SeString> commandAction);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove a chat link handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="commandId">The ID of the command.</param>
|
|
||||||
void RemoveChatLinkHandler(uint commandId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all chat link handlers registered by the plugin.
|
|
||||||
/// </summary>
|
|
||||||
void RemoveChatLinkHandler();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
/// Create a new object of the provided type using its default constructor, then inject objects and properties.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,6 @@ internal class AutoUpdateManager : IServiceType
|
||||||
this.openInstallerWindowLinkTask =
|
this.openInstallerWindowLinkTask =
|
||||||
Service<ChatGui>.GetAsync().ContinueWith(
|
Service<ChatGui>.GetAsync().ContinueWith(
|
||||||
chatGuiTask => chatGuiTask.Result.AddChatLinkHandler(
|
chatGuiTask => chatGuiTask.Result.AddChatLinkHandler(
|
||||||
"Dalamud",
|
|
||||||
1001,
|
|
||||||
(_, _) =>
|
(_, _) =>
|
||||||
{
|
{
|
||||||
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerOpenKind.InstalledPlugins);
|
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(PluginInstallerOpenKind.InstalledPlugins);
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,6 @@ internal class PluginManager : IInternalDisposableService
|
||||||
this.openInstallerWindowPluginChangelogsLink =
|
this.openInstallerWindowPluginChangelogsLink =
|
||||||
Service<ChatGui>.GetAsync().ContinueWith(
|
Service<ChatGui>.GetAsync().ContinueWith(
|
||||||
chatGuiTask => chatGuiTask.Result.AddChatLinkHandler(
|
chatGuiTask => chatGuiTask.Result.AddChatLinkHandler(
|
||||||
"Dalamud",
|
|
||||||
1003,
|
|
||||||
(_, _) =>
|
(_, _) =>
|
||||||
{
|
{
|
||||||
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(
|
Service<DalamudInterface>.GetNullable()?.OpenPluginInstallerTo(
|
||||||
|
|
@ -1294,6 +1292,23 @@ internal class PluginManager : IInternalDisposableService
|
||||||
/// <returns>The calling plugin, or null.</returns>
|
/// <returns>The calling plugin, or null.</returns>
|
||||||
public LocalPlugin? FindCallingPlugin() => this.FindCallingPlugin(new StackTrace());
|
public LocalPlugin? FindCallingPlugin() => this.FindCallingPlugin(new StackTrace());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Notifies all plugins that the active plugins list changed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The invalidation kind.</param>
|
||||||
|
/// <param name="affectedInternalNames">The affected plugins.</param>
|
||||||
|
public void NotifyPluginsForStateChange(PluginListInvalidationKind kind, IEnumerable<string> affectedInternalNames)
|
||||||
|
{
|
||||||
|
foreach (var installedPlugin in this.installedPluginsList)
|
||||||
|
{
|
||||||
|
if (!installedPlugin.IsLoaded || installedPlugin.DalamudInterface == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
installedPlugin.DalamudInterface.NotifyActivePluginsChanged(
|
||||||
|
new ActivePluginsChangedEventArgs(kind, affectedInternalNames));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the services that a plugin may have a dependency on.<br />
|
/// Resolves the services that a plugin may have a dependency on.<br />
|
||||||
/// This is required, as the lifetime of a plugin cannot be longer than PluginManager,
|
/// This is required, as the lifetime of a plugin cannot be longer than PluginManager,
|
||||||
|
|
@ -1823,20 +1838,6 @@ internal class PluginManager : IInternalDisposableService
|
||||||
this.OnInstalledPluginsChanged?.InvokeSafely();
|
this.OnInstalledPluginsChanged?.InvokeSafely();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NotifyPluginsForStateChange(PluginListInvalidationKind kind, IEnumerable<string> affectedInternalNames)
|
|
||||||
{
|
|
||||||
foreach (var installedPlugin in this.installedPluginsList)
|
|
||||||
{
|
|
||||||
if (!installedPlugin.IsLoaded || installedPlugin.DalamudInterface == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
installedPlugin.DalamudInterface.NotifyActivePluginsChanged(
|
|
||||||
kind,
|
|
||||||
// ReSharper disable once PossibleMultipleEnumeration
|
|
||||||
affectedInternalNames.Contains(installedPlugin.Manifest.InternalName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadAndStartLoadSyncPlugins()
|
private void LoadAndStartLoadSyncPlugins()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,9 @@ internal class LocalPlugin : IAsyncDisposable
|
||||||
this.dalamudInterface);
|
this.dalamudInterface);
|
||||||
this.State = PluginState.Loaded;
|
this.State = PluginState.Loaded;
|
||||||
Log.Information("Finished loading {PluginName}", this.InternalName);
|
Log.Information("Finished loading {PluginName}", this.InternalName);
|
||||||
|
|
||||||
|
var manager = Service<PluginManager>.Get();
|
||||||
|
manager.NotifyPluginsForStateChange(PluginListInvalidationKind.Loaded, [this.manifest.InternalName]);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -470,6 +473,9 @@ internal class LocalPlugin : IAsyncDisposable
|
||||||
|
|
||||||
this.State = PluginState.Unloaded;
|
this.State = PluginState.Unloaded;
|
||||||
Log.Information("Finished unloading {PluginName}", this.InternalName);
|
Log.Information("Finished unloading {PluginName}", this.InternalName);
|
||||||
|
|
||||||
|
var manager = Service<PluginManager>.Get();
|
||||||
|
manager.NotifyPluginsForStateChange(PluginListInvalidationKind.Unloaded, [this.manifest.InternalName]);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
namespace Dalamud.Plugin;
|
namespace Dalamud.Plugin;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Causes for a change to the plugin list.
|
/// Causes for a change to the plugin list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum PluginListInvalidationKind
|
public enum PluginListInvalidationKind
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A plugin was loaded.
|
||||||
|
/// </summary>
|
||||||
|
Loaded,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A plugin was unloaded.
|
||||||
|
/// </summary>
|
||||||
|
Unloaded,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An installer-initiated update reloaded plugins.
|
/// An installer-initiated update reloaded plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Game.Addon.Events;
|
using Dalamud.Game.Addon.Events;
|
||||||
|
using Dalamud.Game.Addon.Events.EventDataTypes;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
|
@ -7,15 +8,6 @@ namespace Dalamud.Plugin.Services;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IAddonEventManager
|
public interface IAddonEventManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Delegate to be called when an event is received.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="atkEventType">Event type for this event handler.</param>
|
|
||||||
/// <param name="atkUnitBase">The parent addon for this event handler.</param>
|
|
||||||
/// <param name="atkResNode">The specific node that will trigger this event handler.</param>
|
|
||||||
[Obsolete("Use AddonEventDelegate instead")]
|
|
||||||
public delegate void AddonEventHandler(AddonEventType atkEventType, nint atkUnitBase, nint atkResNode);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate to be called when an event is received.
|
/// Delegate to be called when an event is received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -23,17 +15,6 @@ public interface IAddonEventManager
|
||||||
/// <param name="data">The event data object for use in handling this event.</param>
|
/// <param name="data">The event data object for use in handling this event.</param>
|
||||||
public delegate void AddonEventDelegate(AddonEventType atkEventType, AddonEventData data);
|
public delegate void AddonEventDelegate(AddonEventType atkEventType, AddonEventData data);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers an event handler for the specified addon, node, and type.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="atkUnitBase">The parent addon for this event.</param>
|
|
||||||
/// <param name="atkResNode">The node that will trigger this event.</param>
|
|
||||||
/// <param name="eventType">The event type for this event.</param>
|
|
||||||
/// <param name="eventHandler">The handler to call when event is triggered.</param>
|
|
||||||
/// <returns>IAddonEventHandle used to remove the event. Null if no event was added.</returns>
|
|
||||||
[Obsolete("Use AddEvent with AddonEventDelegate instead of AddonEventHandler")]
|
|
||||||
IAddonEventHandle? AddEvent(nint atkUnitBase, nint atkResNode, AddonEventType eventType, AddonEventHandler eventHandler);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an event handler for the specified addon, node, and type.
|
/// Registers an event handler for the specified addon, node, and type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
|
@ -82,7 +83,25 @@ public interface IChatGui
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the dictionary of Dalamud Link Handlers.
|
/// Gets the dictionary of Dalamud Link Handlers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyDictionary<(string PluginName, uint CommandId), Action<uint, SeString>> RegisteredLinkHandlers { get; }
|
public IReadOnlyDictionary<(string PluginName, Guid CommandId), Action<Guid, SeString>> RegisteredLinkHandlers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a chat link handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandAction">The action to be executed.</param>
|
||||||
|
/// <returns>Returns an SeString payload for the link.</returns>
|
||||||
|
public DalamudLinkPayload AddChatLinkHandler(Action<Guid, SeString> commandAction);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a chat link handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="commandId">The ID of the command.</param>
|
||||||
|
public void RemoveChatLinkHandler(Guid commandId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes all chat link handlers registered by the plugin.
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveChatLinkHandler();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue a chat message. Dalamud will send queued messages on the next framework event.
|
/// Queue a chat message. Dalamud will send queued messages on the next framework event.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
using Dalamud.Game.Gui;
|
using Dalamud.Game.Gui;
|
||||||
|
using Dalamud.Game.NativeWrapper;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
|
||||||
namespace Dalamud.Plugin.Services;
|
namespace Dalamud.Plugin.Services;
|
||||||
|
|
@ -75,37 +76,37 @@ public unsafe interface IGameGui
|
||||||
public bool ScreenToWorld(Vector2 screenPos, out Vector3 worldPos, float rayDistance = 100000.0f);
|
public bool ScreenToWorld(Vector2 screenPos, out Vector3 worldPos, float rayDistance = 100000.0f);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a pointer to the game's UI module.
|
/// Gets a pointer to the game's UIModule instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>IntPtr pointing to UI module.</returns>
|
/// <returns>A pointer wrapper to UIModule.</returns>
|
||||||
public nint GetUIModule();
|
public UIModulePtr GetUIModule();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the pointer to the Addon with the given name and index.
|
/// Gets the pointer to the Addon with the given name and index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">Name of addon to find.</param>
|
/// <param name="name">Name of addon to find.</param>
|
||||||
/// <param name="index">Index of addon to find (1-indexed).</param>
|
/// <param name="index">Index of addon to find (1-indexed).</param>
|
||||||
/// <returns>nint.Zero if unable to find UI, otherwise nint pointing to the start of the addon.</returns>
|
/// <returns>A pointer wrapper to the addon.</returns>
|
||||||
public nint GetAddonByName(string name, int index = 1);
|
public AtkUnitBasePtr GetAddonByName(string name, int index = 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Find the agent associated with an addon, if possible.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">The agent id.</param>
|
||||||
|
/// <returns>A pointer wrapper to the agent interface.</returns>
|
||||||
|
public AgentInterfacePtr GetAgentById(int id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find the agent associated with an addon, if possible.
|
/// Find the agent associated with an addon, if possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="addonName">The addon name.</param>
|
/// <param name="addonName">The addon name.</param>
|
||||||
/// <returns>A pointer to the agent interface.</returns>
|
/// <returns>A pointer wrapper to the agent interface.</returns>
|
||||||
public nint FindAgentInterface(string addonName);
|
public AgentInterfacePtr FindAgentInterface(string addonName);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Find the agent associated with an addon, if possible.
|
/// Find the agent associated with an addon, if possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="addon">The addon address.</param>
|
/// <param name="addon">The addon address.</param>
|
||||||
/// <returns>A pointer to the agent interface.</returns>
|
/// <returns>A pointer wrapper to the agent interface.</returns>
|
||||||
public nint FindAgentInterface(void* addon);
|
public AgentInterfacePtr FindAgentInterface(AtkUnitBasePtr addon);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Find the agent associated with an addon, if possible.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="addonPtr">The addon address.</param>
|
|
||||||
/// <returns>A pointer to the agent interface.</returns>
|
|
||||||
public IntPtr FindAgentInterface(IntPtr addonPtr);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ namespace Dalamud.Plugin.Services;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class handles interacting with game network events.
|
/// This class handles interacting with game network events.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Will be removed in a future release. Use packet handler hooks instead.", true)]
|
||||||
public interface IGameNetwork
|
public interface IGameNetwork
|
||||||
{
|
{
|
||||||
// TODO(v9): we shouldn't be passing pointers to the actual data here
|
// TODO(v9): we shouldn't be passing pointers to the actual data here
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
using Dalamud.Game.Text.Evaluator;
|
using Dalamud.Game.Text.Evaluator;
|
||||||
|
|
@ -11,7 +9,6 @@ namespace Dalamud.Plugin.Services;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a service for retrieving localized text for various in-game entities.
|
/// Defines a service for retrieving localized text for various in-game entities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Experimental("SeStringEvaluator")]
|
|
||||||
public interface ISeStringEvaluator
|
public interface ISeStringEvaluator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,34 @@ using Lumina.Excel.Sheets;
|
||||||
using Lumina.Text;
|
using Lumina.Text;
|
||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Text.ReadOnly;
|
||||||
|
|
||||||
using static Dalamud.Game.Text.SeStringHandling.Payloads.ItemPayload;
|
|
||||||
|
|
||||||
namespace Dalamud.Utility;
|
namespace Dalamud.Utility;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kinds of items that can be fetched from this payload.
|
||||||
|
/// </summary>
|
||||||
|
public enum ItemKind : uint
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Normal items.
|
||||||
|
/// </summary>
|
||||||
|
Normal,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collectible Items.
|
||||||
|
/// </summary>
|
||||||
|
Collectible = 500_000,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// High-Quality items.
|
||||||
|
/// </summary>
|
||||||
|
Hq = 1_000_000,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event/Key items.
|
||||||
|
/// </summary>
|
||||||
|
EventItem = 2_000_000,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Utilities related to Items.
|
/// Utilities related to Items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -624,37 +624,6 @@ public static partial class Util
|
||||||
Win32_PInvoke.FlashWindowEx(flashInfo);
|
Win32_PInvoke.FlashWindowEx(flashInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overwrite text in a file by first writing it to a temporary file, and then
|
|
||||||
/// moving that file to the path specified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the file to write to.</param>
|
|
||||||
/// <param name="text">The text to write.</param>
|
|
||||||
[Api13ToDo("Remove.")]
|
|
||||||
[Obsolete("Replaced with FilesystemUtil.WriteAllTextSafe()")]
|
|
||||||
public static void WriteAllTextSafe(string path, string text) => FilesystemUtil.WriteAllTextSafe(path, text);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overwrite text in a file by first writing it to a temporary file, and then
|
|
||||||
/// moving that file to the path specified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the file to write to.</param>
|
|
||||||
/// <param name="text">The text to write.</param>
|
|
||||||
/// <param name="encoding">Encoding to use.</param>
|
|
||||||
[Api13ToDo("Remove.")]
|
|
||||||
[Obsolete("Replaced with FilesystemUtil.WriteAllTextSafe()")]
|
|
||||||
public static void WriteAllTextSafe(string path, string text, Encoding encoding) => FilesystemUtil.WriteAllTextSafe(path, text, encoding);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Overwrite data in a file by first writing it to a temporary file, and then
|
|
||||||
/// moving that file to the path specified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path of the file to write to.</param>
|
|
||||||
/// <param name="bytes">The data to write.</param>
|
|
||||||
[Api13ToDo("Remove.")]
|
|
||||||
[Obsolete("Replaced with FilesystemUtil.WriteAllBytesSafe()")]
|
|
||||||
public static void WriteAllBytesSafe(string path, byte[] bytes) => FilesystemUtil.WriteAllBytesSafe(path, bytes);
|
|
||||||
|
|
||||||
/// <summary>Gets a temporary file name, for use as the sourceFileName in
|
/// <summary>Gets a temporary file name, for use as the sourceFileName in
|
||||||
/// <see cref="File.Replace(string,string,string?)"/>.</summary>
|
/// <see cref="File.Replace(string,string,string?)"/>.</summary>
|
||||||
/// <param name="targetFile">The target file.</param>
|
/// <param name="targetFile">The target file.</param>
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 6a987dceb781f707d50b7351f6f727b05956343e
|
Subproject commit 2942549605a0b1c7dfb274afabfe7db0332415bc
|
||||||
Loading…
Add table
Add a link
Reference in a new issue