mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-31 21:03:43 +01:00
Prototype, untested
This commit is contained in:
parent
fc2042ea4f
commit
4dabd07131
5 changed files with 393 additions and 46 deletions
63
Dalamud/Game/AddonLifecycle/AddonEvent.cs
Normal file
63
Dalamud/Game/AddonLifecycle/AddonEvent.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
namespace Dalamud.Game.AddonLifecycle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration for available AddonLifecycle events
|
||||||
|
/// </summary>
|
||||||
|
public enum AddonEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired before an addon begins it's setup process.
|
||||||
|
/// </summary>
|
||||||
|
PreSetup,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired after an addon has completed it's setup process.
|
||||||
|
/// </summary>
|
||||||
|
PostSetup,
|
||||||
|
|
||||||
|
// // Events not implemented yet.
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired right before an addon is set to shown.
|
||||||
|
// /// </summary>
|
||||||
|
// PreShow,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired after an addon has been shown.
|
||||||
|
// /// </summary>
|
||||||
|
// PostShow,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired right before an addon is set to hidden.
|
||||||
|
// /// </summary>
|
||||||
|
// PreHide,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired after an addon has been hidden.
|
||||||
|
// /// </summary>
|
||||||
|
// PostHide,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired before an addon begins update.
|
||||||
|
// /// </summary>
|
||||||
|
// PreUpdate,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired after an addon has completed update.
|
||||||
|
// /// </summary>
|
||||||
|
// PostUpdate,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired before an addon begins draw.
|
||||||
|
// /// </summary>
|
||||||
|
// PreDraw,
|
||||||
|
//
|
||||||
|
// /// <summary>
|
||||||
|
// /// Event that is fired after an addon has completed draw.
|
||||||
|
// /// </summary>
|
||||||
|
// PostDraw,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event that is fired before an addon is finalized.
|
||||||
|
/// </summary>
|
||||||
|
PreFinalize,
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
|
|
@ -14,19 +17,29 @@ namespace Dalamud.Game.AddonLifecycle;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycle
|
internal unsafe class AddonLifecycle : IDisposable, IServiceType
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("AddonLifecycle");
|
private static readonly ModuleLog Log = new("AddonLifecycle");
|
||||||
|
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly Framework framework = Service<Framework>.Get();
|
||||||
|
|
||||||
private readonly AddonLifecycleAddressResolver address;
|
private readonly AddonLifecycleAddressResolver address;
|
||||||
private readonly Hook<AddonSetupDelegate> onAddonSetupHook;
|
private readonly Hook<AddonSetupDelegate> onAddonSetupHook;
|
||||||
private readonly Hook<AddonFinalizeDelegate> onAddonFinalizeHook;
|
private readonly Hook<AddonFinalizeDelegate> onAddonFinalizeHook;
|
||||||
|
|
||||||
|
private readonly ConcurrentBag<AddonLifecycleEventListener> newEventListeners = new();
|
||||||
|
private readonly ConcurrentBag<AddonLifecycleEventListener> removeEventListeners = new();
|
||||||
|
private readonly List<AddonLifecycleEventListener> eventListeners = new();
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private AddonLifecycle(SigScanner sigScanner)
|
private AddonLifecycle(SigScanner sigScanner)
|
||||||
{
|
{
|
||||||
this.address = new AddonLifecycleAddressResolver();
|
this.address = new AddonLifecycleAddressResolver();
|
||||||
this.address.Setup(sigScanner);
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
|
this.framework.Update += this.OnFrameworkUpdate;
|
||||||
|
|
||||||
this.onAddonSetupHook = Hook<AddonSetupDelegate>.FromAddress(this.address.AddonSetup, this.OnAddonSetup);
|
this.onAddonSetupHook = Hook<AddonSetupDelegate>.FromAddress(this.address.AddonSetup, this.OnAddonSetup);
|
||||||
this.onAddonFinalizeHook = Hook<AddonFinalizeDelegate>.FromAddress(this.address.AddonFinalize, this.OnAddonFinalize);
|
this.onAddonFinalizeHook = Hook<AddonFinalizeDelegate>.FromAddress(this.address.AddonFinalize, this.OnAddonFinalize);
|
||||||
}
|
}
|
||||||
|
|
@ -35,22 +48,53 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
|
|
||||||
private delegate void AddonFinalizeDelegate(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase);
|
private delegate void AddonFinalizeDelegate(AtkUnitManager* unitManager, AtkUnitBase** atkUnitBase);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPreSetup;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPostSetup;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPreFinalize;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
this.framework.Update -= this.OnFrameworkUpdate;
|
||||||
|
|
||||||
this.onAddonSetupHook.Dispose();
|
this.onAddonSetupHook.Dispose();
|
||||||
this.onAddonFinalizeHook.Dispose();
|
this.onAddonFinalizeHook.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a listener for the target event and addon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The listener to register.</param>
|
||||||
|
internal void RegisterListener(AddonLifecycleEventListener listener)
|
||||||
|
{
|
||||||
|
this.newEventListeners.Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters the listener from events.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="listener">The listener to unregister.</param>
|
||||||
|
internal void UnregisterListener(AddonLifecycleEventListener listener)
|
||||||
|
{
|
||||||
|
this.removeEventListeners.Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to prevent concurrency issues if plugins try to register during iteration of listeners.
|
||||||
|
private void OnFrameworkUpdate(Framework unused)
|
||||||
|
{
|
||||||
|
if (this.newEventListeners.Any())
|
||||||
|
{
|
||||||
|
this.eventListeners.AddRange(this.newEventListeners);
|
||||||
|
this.newEventListeners.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.removeEventListeners.Any())
|
||||||
|
{
|
||||||
|
foreach (var toRemoveListener in this.removeEventListeners)
|
||||||
|
{
|
||||||
|
this.eventListeners.Remove(toRemoveListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.removeEventListeners.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[ServiceManager.CallWhenServicesReady]
|
[ServiceManager.CallWhenServicesReady]
|
||||||
private void ContinueConstruction()
|
private void ContinueConstruction()
|
||||||
{
|
{
|
||||||
|
|
@ -58,6 +102,15 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
this.onAddonFinalizeHook.Enable();
|
this.onAddonFinalizeHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void InvokeListeners(AddonEvent eventType, IAddonLifecycle.AddonArgs args)
|
||||||
|
{
|
||||||
|
// Match on string.empty for listeners that want events for all addons.
|
||||||
|
foreach (var listener in this.eventListeners.Where(listener => listener.EventType == eventType && (listener.AddonName == args.AddonName || listener.AddonName == string.Empty)))
|
||||||
|
{
|
||||||
|
listener.FunctionDelegate.Invoke(eventType, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private nint OnAddonSetup(AtkUnitBase* addon)
|
private nint OnAddonSetup(AtkUnitBase* addon)
|
||||||
{
|
{
|
||||||
if (addon is null)
|
if (addon is null)
|
||||||
|
|
@ -65,7 +118,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.AddonPreSetup?.Invoke(new IAddonLifecycle.AddonArgs { Addon = (nint)addon });
|
this.InvokeListeners(AddonEvent.PreSetup, new IAddonLifecycle.AddonArgs { Addon = (nint)addon });
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -76,7 +129,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.AddonPostSetup?.Invoke(new IAddonLifecycle.AddonArgs { Addon = (nint)addon });
|
this.InvokeListeners(AddonEvent.PostSetup, new IAddonLifecycle.AddonArgs { Addon = (nint)addon });
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -96,7 +149,7 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.AddonPreFinalize?.Invoke(new IAddonLifecycle.AddonArgs { Addon = (nint)atkUnitBase[0] });
|
this.InvokeListeners(AddonEvent.PreFinalize, new IAddonLifecycle.AddonArgs { Addon = (nint)atkUnitBase[0] });
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
@ -118,39 +171,93 @@ internal unsafe class AddonLifecycle : IDisposable, IServiceType, IAddonLifecycl
|
||||||
#pragma warning restore SA1015
|
#pragma warning restore SA1015
|
||||||
internal class AddonLifecyclePluginScoped : IDisposable, IServiceType, IAddonLifecycle
|
internal class AddonLifecyclePluginScoped : IDisposable, IServiceType, IAddonLifecycle
|
||||||
{
|
{
|
||||||
|
private static readonly ModuleLog Log = new("AddonLifecycle:PluginScoped");
|
||||||
|
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly AddonLifecycle addonLifecycleService = Service<AddonLifecycle>.Get();
|
private readonly AddonLifecycle addonLifecycleService = Service<AddonLifecycle>.Get();
|
||||||
|
|
||||||
/// <summary>
|
private readonly List<AddonLifecycleEventListener> eventListeners = new();
|
||||||
/// Initializes a new instance of the <see cref="AddonLifecyclePluginScoped"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public AddonLifecyclePluginScoped()
|
|
||||||
{
|
|
||||||
this.addonLifecycleService.AddonPreSetup += this.AddonPreSetupForward;
|
|
||||||
this.addonLifecycleService.AddonPostSetup += this.AddonPostSetupForward;
|
|
||||||
this.addonLifecycleService.AddonPreFinalize += this.AddonPreFinalizeForward;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPreSetup;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPostSetup;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public event Action<IAddonLifecycle.AddonArgs>? AddonPreFinalize;
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
this.addonLifecycleService.AddonPreSetup -= this.AddonPreSetupForward;
|
foreach (var listener in this.eventListeners)
|
||||||
this.addonLifecycleService.AddonPostSetup -= this.AddonPostSetupForward;
|
{
|
||||||
this.addonLifecycleService.AddonPreFinalize -= this.AddonPreFinalizeForward;
|
this.addonLifecycleService.UnregisterListener(listener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddonPreSetupForward(IAddonLifecycle.AddonArgs args) => this.AddonPreSetup?.Invoke(args);
|
/// <inheritdoc/>
|
||||||
|
public void RegisterListener(AddonEvent eventType, IEnumerable<string> addonNames, IAddonLifecycle.AddonEventDelegate handler)
|
||||||
|
{
|
||||||
|
foreach (var addonName in addonNames)
|
||||||
|
{
|
||||||
|
this.RegisterListener(eventType, addonName, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void AddonPostSetupForward(IAddonLifecycle.AddonArgs args) => this.AddonPostSetup?.Invoke(args);
|
/// <inheritdoc/>
|
||||||
|
public void RegisterListener(AddonEvent eventType, string addonName, IAddonLifecycle.AddonEventDelegate handler)
|
||||||
|
{
|
||||||
|
var listener = new AddonLifecycleEventListener(eventType, addonName, handler);
|
||||||
|
this.eventListeners.Add(listener);
|
||||||
|
this.addonLifecycleService.RegisterListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddonPreFinalizeForward(IAddonLifecycle.AddonArgs args) => this.AddonPreFinalize?.Invoke(args);
|
/// <inheritdoc/>
|
||||||
|
public void RegisterListener(AddonEvent eventType, IAddonLifecycle.AddonEventDelegate handler)
|
||||||
|
{
|
||||||
|
this.RegisterListener(eventType, string.Empty, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void UnregisterListener(AddonEvent eventType, IEnumerable<string> addonNames, IAddonLifecycle.AddonEventDelegate? handler = null)
|
||||||
|
{
|
||||||
|
foreach (var addonName in addonNames)
|
||||||
|
{
|
||||||
|
this.UnregisterListener(eventType, addonName, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void UnregisterListener(AddonEvent eventType, string addonName, IAddonLifecycle.AddonEventDelegate? handler = null)
|
||||||
|
{
|
||||||
|
// This style is simpler to read imo. If the handler is null we want all entries,
|
||||||
|
// if they specified a handler then only the specific entries with that handler.
|
||||||
|
var targetListeners = this.eventListeners
|
||||||
|
.Where(entry => entry.EventType == eventType)
|
||||||
|
.Where(entry => entry.AddonName == addonName)
|
||||||
|
.Where(entry => handler is null || entry.FunctionDelegate == handler);
|
||||||
|
|
||||||
|
foreach (var listener in targetListeners)
|
||||||
|
{
|
||||||
|
this.addonLifecycleService.UnregisterListener(listener);
|
||||||
|
this.eventListeners.Remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void UnregisterListener(AddonEvent eventType, IAddonLifecycle.AddonEventDelegate? handler = null)
|
||||||
|
{
|
||||||
|
this.UnregisterListener(eventType, string.Empty, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void UnregisterListener(IAddonLifecycle.AddonEventDelegate handler, params IAddonLifecycle.AddonEventDelegate[] handlers)
|
||||||
|
{
|
||||||
|
foreach (var listener in this.eventListeners.Where(entry => entry.FunctionDelegate == handler))
|
||||||
|
{
|
||||||
|
this.addonLifecycleService.UnregisterListener(listener);
|
||||||
|
this.eventListeners.Remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var handlerParma in handlers)
|
||||||
|
{
|
||||||
|
foreach (var listener in this.eventListeners.Where(entry => entry.FunctionDelegate == handlerParma))
|
||||||
|
{
|
||||||
|
this.addonLifecycleService.UnregisterListener(listener);
|
||||||
|
this.eventListeners.Remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs
Normal file
38
Dalamud/Game/AddonLifecycle/AddonLifecycleEventListener.cs
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Dalamud.Plugin.Services;
|
||||||
|
|
||||||
|
namespace Dalamud.Game.AddonLifecycle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class is a helper for tracking and invoking listener delegates.
|
||||||
|
/// </summary>
|
||||||
|
internal class AddonLifecycleEventListener
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddonLifecycleEventListener"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">Event type to listen for.</param>
|
||||||
|
/// <param name="addonName">Addon name to listen for.</param>
|
||||||
|
/// <param name="functionDelegate">Delegate to invoke.</param>
|
||||||
|
internal AddonLifecycleEventListener(AddonEvent eventType, string addonName, IAddonLifecycle.AddonEventDelegate functionDelegate)
|
||||||
|
{
|
||||||
|
this.EventType = eventType;
|
||||||
|
this.AddonName = addonName;
|
||||||
|
this.FunctionDelegate = functionDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the addon this listener is looking for.
|
||||||
|
/// string.Empty if it wants to be called for any addon.
|
||||||
|
/// </summary>
|
||||||
|
public string AddonName { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the event type this listener is looking for.
|
||||||
|
/// </summary>
|
||||||
|
public AddonEvent EventType { get; init; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the delegate this listener invokes.
|
||||||
|
/// </summary>
|
||||||
|
public IAddonLifecycle.AddonEventDelegate FunctionDelegate { get; init; }
|
||||||
|
}
|
||||||
83
Dalamud/Hooking/Internal/CallHook.cs
Normal file
83
Dalamud/Hooking/Internal/CallHook.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using Reloaded.Hooks.Definitions;
|
||||||
|
|
||||||
|
namespace Dalamud.Hooking.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hooking class for callsite hooking. This hook does not have capabilities of calling the original function.
|
||||||
|
/// The intended use is replacing virtual function calls where you are able to manually invoke the original call using the delegate arguments.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Delegate signature for this hook.</typeparam>
|
||||||
|
internal class CallHook<T> : IDisposable where T : Delegate
|
||||||
|
{
|
||||||
|
private readonly Reloaded.Hooks.AsmHook asmHook;
|
||||||
|
|
||||||
|
private T? detour;
|
||||||
|
private bool activated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CallHook{T}"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Address of the instruction to replace.</param>
|
||||||
|
/// <param name="detour">Delegate to invoke.</param>
|
||||||
|
internal CallHook(nint address, T detour)
|
||||||
|
{
|
||||||
|
this.detour = detour;
|
||||||
|
|
||||||
|
var detourPtr = Marshal.GetFunctionPointerForDelegate(this.detour);
|
||||||
|
var code = new[]
|
||||||
|
{
|
||||||
|
"use64",
|
||||||
|
$"mov rax, 0x{detourPtr:X8}",
|
||||||
|
"call rax",
|
||||||
|
};
|
||||||
|
|
||||||
|
var opt = new AsmHookOptions
|
||||||
|
{
|
||||||
|
PreferRelativeJump = true,
|
||||||
|
Behaviour = Reloaded.Hooks.Definitions.Enums.AsmHookBehaviour.DoNotExecuteOriginal,
|
||||||
|
MaxOpcodeSize = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.asmHook = new Reloaded.Hooks.AsmHook(code, (nuint)address, opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether or not the hook is enabled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsEnabled => this.asmHook.IsEnabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts intercepting a call to the function.
|
||||||
|
/// </summary>
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
if (!this.activated)
|
||||||
|
{
|
||||||
|
this.activated = true;
|
||||||
|
this.asmHook.Activate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.asmHook.Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stops intercepting a call to the function.
|
||||||
|
/// </summary>
|
||||||
|
public void Disable()
|
||||||
|
{
|
||||||
|
this.asmHook.Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a hook from the current process.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.asmHook.Disable();
|
||||||
|
this.detour = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using Dalamud.Game.AddonLifecycle;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||||
|
|
||||||
|
|
@ -11,19 +13,73 @@ namespace Dalamud.Plugin.Services;
|
||||||
public interface IAddonLifecycle
|
public interface IAddonLifecycle
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that fires before an addon is being setup.
|
/// Delegate for receiving addon lifecycle event messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<AddonArgs> AddonPreSetup;
|
/// <param name="eventType">The event type that triggered the message.</param>
|
||||||
|
/// <param name="addonInfo">Information about what addon triggered the message.</param>
|
||||||
|
public delegate void AddonEventDelegate(AddonEvent eventType, AddonArgs addonInfo);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that fires after an addon is done being setup.
|
/// Register a listener that will trigger on the specified event and any of the specified addons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<AddonArgs> AddonPostSetup;
|
/// <param name="eventType">Event type to trigger on.</param>
|
||||||
|
/// <param name="addonNames">Addon names that will trigger the handler to be invoked.</param>
|
||||||
|
/// <param name="handler">The handler to invoke.</param>
|
||||||
|
void RegisterListener(AddonEvent eventType, IEnumerable<string> addonNames, AddonEventDelegate handler);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event that fires before an addon is being finalized.
|
/// Register a listener that will trigger on the specified event only for the specified addon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<AddonArgs> AddonPreFinalize;
|
/// <param name="eventType">Event type to trigger on.</param>
|
||||||
|
/// <param name="addonName">The addon name that will trigger the handler to be invoked.</param>
|
||||||
|
/// <param name="handler">The handler to invoke.</param>
|
||||||
|
void RegisterListener(AddonEvent eventType, string addonName, AddonEventDelegate handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a listener that will trigger on the specified event for any addon.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventType">Event type to trigger on.</param>
|
||||||
|
/// <param name="handler">The handler to invoke.</param>
|
||||||
|
void RegisterListener(AddonEvent eventType, AddonEventDelegate handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister listener from specified event type and specified addon names.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a specific handler is not provided, all handlers for the event type and addon names will be unregistered.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="eventType">Event type to deregister.</param>
|
||||||
|
/// <param name="addonNames">Addon names to deregister.</param>
|
||||||
|
/// <param name="handler">Optional specific handler to remove.</param>
|
||||||
|
void UnregisterListener(AddonEvent eventType, IEnumerable<string> addonNames, [Optional] AddonEventDelegate handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister all listeners for the specified event type and addon name.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a specific handler is not provided, all handlers for the event type and addons will be unregistered.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="eventType">Event type to deregister.</param>
|
||||||
|
/// <param name="addonName">Addon name to deregister.</param>
|
||||||
|
/// <param name="handler">Optional specific handler to remove.</param>
|
||||||
|
void UnregisterListener(AddonEvent eventType, string addonName, [Optional] AddonEventDelegate handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister an event type handler.<br/>This will only remove a handler that is added via <see cref="RegisterListener(AddonEvent, AddonEventDelegate)"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a specific handler is not provided, all handlers for the event type and addons will be unregistered.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="eventType">Event type to deregister.</param>
|
||||||
|
/// <param name="handler">Optional specific handler to remove.</param>
|
||||||
|
void UnregisterListener(AddonEvent eventType, [Optional] AddonEventDelegate handler);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregister all events that use the specified handlers.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="handler">Event handler to remove.</param>
|
||||||
|
/// <param name="handlers">Additional handlers to remove.</param>
|
||||||
|
void UnregisterListener(AddonEventDelegate handler, params AddonEventDelegate[] handlers);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Addon argument data for use in event subscribers.
|
/// Addon argument data for use in event subscribers.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue