mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-25 14:11:48 +01:00
Fix Addon/Agent Lifecycle Register/Unregister
This commit is contained in:
parent
05969f02ad
commit
a68248fdf8
2 changed files with 116 additions and 77 deletions
|
|
@ -31,7 +31,7 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
private readonly Framework framework = Service<Framework>.Get();
|
private readonly Framework framework = Service<Framework>.Get();
|
||||||
|
|
||||||
private Hook<AtkUnitBase.Delegates.Initialize>? onInitializeAddonHook;
|
private Hook<AtkUnitBase.Delegates.Initialize>? onInitializeAddonHook;
|
||||||
private bool isInvokingListeners = false;
|
private bool isInvokingListeners;
|
||||||
|
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private AddonLifecycle()
|
private AddonLifecycle()
|
||||||
|
|
@ -56,29 +56,33 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
AllocatedTables.Clear();
|
AllocatedTables.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a virtual table address to the original virtual table address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableAddress">The modified address to resolve.</param>
|
||||||
|
/// <returns>The original address.</returns>
|
||||||
|
internal static AtkUnitBase.AtkUnitBaseVirtualTable* GetOriginalVirtualTable(AtkUnitBase.AtkUnitBaseVirtualTable* tableAddress)
|
||||||
|
{
|
||||||
|
var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress);
|
||||||
|
if (matchedTable == null) return null;
|
||||||
|
|
||||||
|
return matchedTable.OriginalVirtualTable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a listener for the target event and addon.
|
/// Register a listener for the target event and addon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="listener">The listener to register.</param>
|
/// <param name="listener">The listener to register.</param>
|
||||||
internal void RegisterListener(AddonLifecycleEventListener listener)
|
internal void RegisterListener(AddonLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() =>
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
if (!this.EventListeners.ContainsKey(listener.EventType))
|
this.framework.RunOnTick(() => this.RegisterListenerMethod(listener));
|
||||||
{
|
}
|
||||||
if (!this.EventListeners.TryAdd(listener.EventType, []))
|
else
|
||||||
return;
|
{
|
||||||
}
|
this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener));
|
||||||
|
}
|
||||||
// Note: string.Empty is a valid addon name, as that will trigger on any addon for this event type
|
|
||||||
if (!this.EventListeners[listener.EventType].ContainsKey(listener.AddonName))
|
|
||||||
{
|
|
||||||
if (!this.EventListeners[listener.EventType].TryAdd(listener.AddonName, []))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.EventListeners[listener.EventType][listener.AddonName].Add(listener);
|
|
||||||
}, delayTicks: this.isInvokingListeners ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -87,16 +91,14 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
/// <param name="listener">The listener to unregister.</param>
|
/// <param name="listener">The listener to unregister.</param>
|
||||||
internal void UnregisterListener(AddonLifecycleEventListener listener)
|
internal void UnregisterListener(AddonLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() =>
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners))
|
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
||||||
{
|
}
|
||||||
if (addonListeners.TryGetValue(listener.AddonName, out var addonListener))
|
else
|
||||||
{
|
{
|
||||||
addonListener.Remove(listener);
|
this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}, delayTicks: this.isInvokingListeners ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -147,17 +149,38 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
|
||||||
this.isInvokingListeners = false;
|
this.isInvokingListeners = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private void RegisterListenerMethod(AddonLifecycleEventListener listener)
|
||||||
/// Resolves a virtual table address to the original virtual table address.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tableAddress">The modified address to resolve.</param>
|
|
||||||
/// <returns>The original address.</returns>
|
|
||||||
internal AtkUnitBase.AtkUnitBaseVirtualTable* GetOriginalVirtualTable(AtkUnitBase.AtkUnitBaseVirtualTable* tableAddress)
|
|
||||||
{
|
{
|
||||||
var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress);
|
if (!this.EventListeners.ContainsKey(listener.EventType))
|
||||||
if (matchedTable == null) return null;
|
{
|
||||||
|
if (!this.EventListeners.TryAdd(
|
||||||
|
listener.EventType,
|
||||||
|
[
|
||||||
|
])) return;
|
||||||
|
}
|
||||||
|
|
||||||
return matchedTable.OriginalVirtualTable;
|
// Note: string.Empty is a valid addon name, as that will trigger on any addon for this event type
|
||||||
|
if (!this.EventListeners[listener.EventType].ContainsKey(listener.AddonName))
|
||||||
|
{
|
||||||
|
if (!this.EventListeners[listener.EventType]
|
||||||
|
.TryAdd(
|
||||||
|
listener.AddonName,
|
||||||
|
[
|
||||||
|
])) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.EventListeners[listener.EventType][listener.AddonName].Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterListenerMethod(AddonLifecycleEventListener listener)
|
||||||
|
{
|
||||||
|
if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners))
|
||||||
|
{
|
||||||
|
if (addonListeners.TryGetValue(listener.AddonName, out var addonListener))
|
||||||
|
{
|
||||||
|
addonListener.Remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAddonInitialize(AtkUnitBase* addon)
|
private void OnAddonInitialize(AtkUnitBase* addon)
|
||||||
|
|
@ -277,5 +300,5 @@ internal class AddonLifecyclePluginScoped : IInternalDisposableService, IAddonLi
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress)
|
public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress)
|
||||||
=> (nint)this.addonLifecycleService.GetOriginalVirtualTable((AtkUnitBase.AtkUnitBaseVirtualTable*)virtualTableAddress);
|
=> (nint)AddonLifecycle.GetOriginalVirtualTable((AtkUnitBase.AtkUnitBaseVirtualTable*)virtualTableAddress);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,30 +69,33 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
AllocatedTables.Clear();
|
AllocatedTables.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a virtual table address to the original virtual table address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tableAddress">The modified address to resolve.</param>
|
||||||
|
/// <returns>The original address.</returns>
|
||||||
|
internal static AgentInterface.AgentInterfaceVirtualTable* GetOriginalVirtualTable(AgentInterface.AgentInterfaceVirtualTable* tableAddress)
|
||||||
|
{
|
||||||
|
var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress);
|
||||||
|
if (matchedTable == null) return null;
|
||||||
|
|
||||||
|
return matchedTable.OriginalVirtualTable;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a listener for the target event and agent.
|
/// Register a listener for the target event and agent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="listener">The listener to register.</param>
|
/// <param name="listener">The listener to register.</param>
|
||||||
internal void RegisterListener(AgentLifecycleEventListener listener)
|
internal void RegisterListener(AgentLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() =>
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
if (!this.EventListeners.ContainsKey(listener.EventType))
|
this.framework.RunOnTick(() => this.RegisterListenerMethod(listener));
|
||||||
{
|
}
|
||||||
if (!this.EventListeners.TryAdd(listener.EventType, []))
|
else
|
||||||
return;
|
{
|
||||||
}
|
this.framework.RunOnFrameworkThread(() => this.RegisterListenerMethod(listener));
|
||||||
|
}
|
||||||
// Note: uint.MaxValue is a valid agent id, as that will trigger on any agent for this event type
|
|
||||||
if (!this.EventListeners[listener.EventType].ContainsKey(listener.AgentId))
|
|
||||||
{
|
|
||||||
if (!this.EventListeners[listener.EventType].TryAdd(listener.AgentId, []))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.EventListeners[listener.EventType][listener.AgentId].Add(listener);
|
|
||||||
},
|
|
||||||
delayTicks: this.isInvokingListeners ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -101,17 +104,14 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
/// <param name="listener">The listener to unregister.</param>
|
/// <param name="listener">The listener to unregister.</param>
|
||||||
internal void UnregisterListener(AgentLifecycleEventListener listener)
|
internal void UnregisterListener(AgentLifecycleEventListener listener)
|
||||||
{
|
{
|
||||||
this.framework.RunOnTick(() =>
|
if (this.isInvokingListeners)
|
||||||
{
|
{
|
||||||
if (this.EventListeners.TryGetValue(listener.EventType, out var agentListeners))
|
this.framework.RunOnTick(() => this.UnregisterListenerMethod(listener));
|
||||||
{
|
}
|
||||||
if (agentListeners.TryGetValue(listener.AgentId, out var agentListener))
|
else
|
||||||
{
|
{
|
||||||
agentListener.Remove(listener);
|
this.framework.RunOnFrameworkThread(() => this.UnregisterListenerMethod(listener));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
delayTicks: this.isInvokingListeners ? 1 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -162,19 +162,6 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
this.isInvokingListeners = false;
|
this.isInvokingListeners = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resolves a virtual table address to the original virtual table address.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tableAddress">The modified address to resolve.</param>
|
|
||||||
/// <returns>The original address.</returns>
|
|
||||||
internal AgentInterface.AgentInterfaceVirtualTable* GetOriginalVirtualTable(AgentInterface.AgentInterfaceVirtualTable* tableAddress)
|
|
||||||
{
|
|
||||||
var matchedTable = AllocatedTables.FirstOrDefault(table => table.ModifiedVirtualTable == tableAddress);
|
|
||||||
if (matchedTable == null) return null;
|
|
||||||
|
|
||||||
return matchedTable.OriginalVirtualTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAgentModuleInitialize(AgentModule* thisPtr, UIModule* uiModule)
|
private void OnAgentModuleInitialize(AgentModule* thisPtr, UIModule* uiModule)
|
||||||
{
|
{
|
||||||
this.onInitializeAgentsHook!.Original(thisPtr, uiModule);
|
this.onInitializeAgentsHook!.Original(thisPtr, uiModule);
|
||||||
|
|
@ -193,6 +180,35 @@ internal unsafe class AgentLifecycle : IInternalDisposableService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterListenerMethod(AgentLifecycleEventListener listener)
|
||||||
|
{
|
||||||
|
if (!this.EventListeners.ContainsKey(listener.EventType))
|
||||||
|
{
|
||||||
|
if (!this.EventListeners.TryAdd(listener.EventType, []))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: uint.MaxValue is a valid agent id, as that will trigger on any agent for this event type
|
||||||
|
if (!this.EventListeners[listener.EventType].ContainsKey(listener.AgentId))
|
||||||
|
{
|
||||||
|
if (!this.EventListeners[listener.EventType].TryAdd(listener.AgentId, []))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.EventListeners[listener.EventType][listener.AgentId].Add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnregisterListenerMethod(AgentLifecycleEventListener listener)
|
||||||
|
{
|
||||||
|
if (this.EventListeners.TryGetValue(listener.EventType, out var agentListeners))
|
||||||
|
{
|
||||||
|
if (agentListeners.TryGetValue(listener.AgentId, out var agentListener))
|
||||||
|
{
|
||||||
|
agentListener.Remove(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ReplaceVirtualTables(AgentModule* agentModule)
|
private void ReplaceVirtualTables(AgentModule* agentModule)
|
||||||
{
|
{
|
||||||
foreach (uint index in Enumerable.Range(0, agentModule->Agents.Length))
|
foreach (uint index in Enumerable.Range(0, agentModule->Agents.Length))
|
||||||
|
|
@ -311,5 +327,5 @@ internal class AgentLifecyclePluginScoped : IInternalDisposableService, IAgentLi
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress)
|
public unsafe nint GetOriginalVirtualTable(nint virtualTableAddress)
|
||||||
=> (nint)this.agentLifecycleService.GetOriginalVirtualTable((AgentInterface.AgentInterfaceVirtualTable*)virtualTableAddress);
|
=> (nint)AgentLifecycle.GetOriginalVirtualTable((AgentInterface.AgentInterfaceVirtualTable*)virtualTableAddress);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue