diff --git a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
index 403671920..e38f56921 100644
--- a/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
+++ b/Dalamud/Game/Addon/Lifecycle/AddonLifecycle.cs
@@ -38,7 +38,8 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
///
/// Gets a list of all AddonLifecycle Event Listeners.
///
- internal Dictionary> EventListeners { get; } = [];
+ /// Mapping is: EventType -> AddonName -> ListenerList
+ internal Dictionary>> EventListeners { get; } = [];
///
void IInternalDisposableService.DisposeService()
@@ -61,8 +62,18 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
/// The listener to register.
internal void RegisterListener(AddonLifecycleEventListener listener)
{
- this.EventListeners.TryAdd(listener.EventType, [ listener ]);
- this.EventListeners[listener.EventType].Add(listener);
+ if (!this.EventListeners.ContainsKey(listener.EventType))
+ {
+ this.EventListeners.TryAdd(listener.EventType, []);
+ }
+
+ // 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))
+ {
+ this.EventListeners[listener.EventType].TryAdd(listener.AddonName, []);
+ }
+
+ this.EventListeners[listener.EventType][listener.AddonName].Add(listener);
}
///
@@ -71,9 +82,12 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
/// The listener to unregister.
internal void UnregisterListener(AddonLifecycleEventListener listener)
{
- if (this.EventListeners.TryGetValue(listener.EventType, out var listenerList))
+ if (this.EventListeners.TryGetValue(listener.EventType, out var addonListeners))
{
- listenerList.Remove(listener);
+ if (addonListeners.TryGetValue(listener.AddonName, out var addonListener))
+ {
+ addonListener.Remove(listener);
+ }
}
}
@@ -86,22 +100,37 @@ internal unsafe class AddonLifecycle : IInternalDisposableService
internal void InvokeListenersSafely(AddonEvent eventType, AddonArgs args, [CallerMemberName] string blame = "")
{
// Early return if we don't have any listeners of this type
- if (!this.EventListeners.TryGetValue(eventType, out var listenerList)) return;
+ if (!this.EventListeners.TryGetValue(eventType, out var addonListeners)) return;
- // Do not use linq; this is a high-traffic function, and more heap allocations avoided, the better.
- foreach (var listener in listenerList)
+ // Handle listeners for this event type that don't care which addon is triggering it
+ if (addonListeners.TryGetValue(string.Empty, out var globalListeners))
{
- // Match on string.empty for listeners that want events for all addons.
- if (!string.IsNullOrWhiteSpace(listener.AddonName) && !args.IsAddon(listener.AddonName))
- continue;
-
- try
+ foreach (var listener in globalListeners)
{
- listener.FunctionDelegate.Invoke(eventType, args);
+ try
+ {
+ listener.FunctionDelegate.Invoke(eventType, args);
+ }
+ catch (Exception e)
+ {
+ Log.Error(e, $"Exception in {blame} during {eventType} invoke, for global addon event listener.");
+ }
}
- catch (Exception e)
+ }
+
+ // Handle listeners that are listening for this addon and event type specifically
+ if (addonListeners.TryGetValue(args.AddonName, out var addonListener))
+ {
+ foreach (var listener in addonListener)
{
- Log.Error(e, $"Exception in {blame} during {eventType} invoke.");
+ try
+ {
+ listener.FunctionDelegate.Invoke(eventType, args);
+ }
+ catch (Exception e)
+ {
+ Log.Error(e, $"Exception in {blame} during {eventType} invoke, for specific addon {args.AddonName}.");
+ }
}
}
}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs
index 73c4e540a..0f193556b 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonLifecycleWidget.cs
@@ -2,7 +2,8 @@ using System.Diagnostics.CodeAnalysis;
using Dalamud.Bindings.ImGui;
using Dalamud.Game.Addon.Lifecycle;
-using Dalamud.Interface.Utility;
+using Dalamud.Interface.Utility.Raii;
+using Dalamud.Utility;
namespace Dalamud.Interface.Internal.Windows.Data.Widgets;
@@ -57,35 +58,38 @@ public class AddonLifecycleWidget : IDataWindowWidget
{
if (!this.Ready) return;
- foreach (var (listenerType, listeners) in this.AddonLifecycle.EventListeners)
+ foreach (var (eventType, addonListeners) in this.AddonLifecycle.EventListeners)
{
- if (ImGui.CollapsingHeader(listenerType.ToString()))
+ using var eventId = ImRaii.PushId(eventType.ToString());
+
+ if (ImGui.CollapsingHeader(eventType.ToString()))
{
- ImGui.Indent();
+ using var eventIndent = ImRaii.PushIndent();
- if (listeners.Count == 0)
+ if (addonListeners.Count == 0)
{
- ImGui.Text("No Listeners Registered for Event"u8);
+ ImGui.Text("No Addons Registered for Event"u8);
}
- if (ImGui.BeginTable("AddonLifecycleListenersTable"u8, 2))
+ foreach (var (addonName, listeners) in addonListeners)
{
- ImGui.TableSetupColumn("##AddonName"u8, ImGuiTableColumnFlags.WidthFixed, 100.0f * ImGuiHelpers.GlobalScale);
- ImGui.TableSetupColumn("##MethodInvoke"u8, ImGuiTableColumnFlags.WidthStretch);
+ using var addonId = ImRaii.PushId(addonName);
- foreach (var listener in listeners)
+ if (ImGui.CollapsingHeader(addonName.IsNullOrEmpty() ? "GLOBAL" : addonName))
{
- ImGui.TableNextColumn();
- ImGui.Text(listener.AddonName is "" ? "GLOBAL" : listener.AddonName);
+ using var addonIndent = ImRaii.PushIndent();
- ImGui.TableNextColumn();
- ImGui.Text($"{listener.FunctionDelegate.Method.DeclaringType?.FullName ?? "Unknown Declaring Type"}::{listener.FunctionDelegate.Method.Name}");
+ if (listeners.Count == 0)
+ {
+ ImGui.Text("No Listeners Registered for Event"u8);
+ }
+
+ foreach (var listener in listeners)
+ {
+ ImGui.Text($"{listener.FunctionDelegate.Method.DeclaringType?.FullName ?? "Unknown Declaring Type"}::{listener.FunctionDelegate.Method.Name}");
+ }
}
-
- ImGui.EndTable();
}
-
- ImGui.Unindent();
}
}
}