diff --git a/Dalamud/Configuration/Internal/DalamudConfiguration.cs b/Dalamud/Configuration/Internal/DalamudConfiguration.cs index 168382fab..8dafd897e 100644 --- a/Dalamud/Configuration/Internal/DalamudConfiguration.cs +++ b/Dalamud/Configuration/Internal/DalamudConfiguration.cs @@ -650,6 +650,16 @@ internal sealed class DalamudConfiguration : IInternalDisposableService } }); - this.DalamudConfigurationSaved?.Invoke(this); + foreach (var action in Delegate.EnumerateInvocationList(this.DalamudConfigurationSaved)) + { + try + { + action(this); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } + } } } diff --git a/Dalamud/Console/ConsoleManager.cs b/Dalamud/Console/ConsoleManager.cs index c79a104e1..377bac208 100644 --- a/Dalamud/Console/ConsoleManager.cs +++ b/Dalamud/Console/ConsoleManager.cs @@ -184,8 +184,18 @@ internal partial class ConsoleManager : IServiceType /// Whether the command was successfully processed. public bool ProcessCommand(string command) { - if (this.Invoke?.Invoke(command) == true) - return true; + foreach (var action in Delegate.EnumerateInvocationList(this.Invoke)) + { + try + { + if (action(command)) + return true; + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } + } var matches = GetCommandParsingRegex().Matches(command); if (matches.Count == 0) diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs index 5101657ba..4c32873c6 100644 --- a/Dalamud/Game/ClientState/ClientState.cs +++ b/Dalamud/Game/ClientState/ClientState.cs @@ -42,10 +42,10 @@ internal sealed class ClientState : IInternalDisposableService, IClientState [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); - + [ServiceManager.ServiceDependency] private readonly NetworkHandlers networkHandlers = Service.Get(); - + private bool lastConditionNone = true; [ServiceManager.ServiceConstructor] @@ -176,7 +176,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.uiModuleHandlePacketHook.Dispose(); this.onLogoutHook.Dispose(); - this.framework.Update -= this.FrameworkOnOnUpdateEvent; + this.framework.Update -= this.FrameworkOnOnUpdateEvent; this.networkHandlers.CfPop -= this.NetworkHandlersOnCfPop; } @@ -211,50 +211,51 @@ internal sealed class ClientState : IInternalDisposableService, IClientState this.setupTerritoryTypeHook.Original(eventFramework, territoryType); } - private unsafe void UIModuleHandlePacketDetour(UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet) + private unsafe void UIModuleHandlePacketDetour( + UIModule* thisPtr, UIModulePacketType type, uint uintParam, void* packet) { this.uiModuleHandlePacketHook.Original(thisPtr, type, uintParam, packet); switch (type) { - case UIModulePacketType.ClassJobChange when this.ClassJobChanged is { } callback: + case UIModulePacketType.ClassJobChange: + { + var classJobId = uintParam; + + foreach (var action in Delegate.EnumerateInvocationList(this.ClassJobChanged)) { - var classJobId = uintParam; - - foreach (var action in callback.GetInvocationList().Cast()) + try { - try - { - action(classJobId); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); - } + action(classJobId); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); } - - break; } - case UIModulePacketType.LevelChange when this.LevelChanged is { } callback: + break; + } + + case UIModulePacketType.LevelChange: + { + var classJobId = *(uint*)packet; + var level = *(ushort*)((nint)packet + 4); + + foreach (var action in Delegate.EnumerateInvocationList(this.LevelChanged)) { - var classJobId = *(uint*)packet; - var level = *(ushort*)((nint)packet + 4); - - foreach (var action in callback.GetInvocationList().Cast()) + try { - try - { - action(classJobId, level); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); - } + action(classJobId, level); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); } - - break; } + + break; + } } } @@ -291,18 +292,15 @@ internal sealed class ClientState : IInternalDisposableService, IClientState Log.Debug("Logout: Type {type}, Code {code}", type, code); - if (this.Logout is { } callback) + foreach (var action in Delegate.EnumerateInvocationList(this.Logout)) { - foreach (var action in callback.GetInvocationList().Cast()) + try { - try - { - action(type, code); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); - } + action(type, code); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); } } diff --git a/Dalamud/Game/ClientState/Conditions/Condition.cs b/Dalamud/Game/ClientState/Conditions/Condition.cs index 8f39df46f..99748f71b 100644 --- a/Dalamud/Game/ClientState/Conditions/Condition.cs +++ b/Dalamud/Game/ClientState/Conditions/Condition.cs @@ -22,7 +22,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition [ServiceManager.ServiceDependency] private readonly Framework framework = Service.Get(); - + private readonly bool[] cache = new bool[MaxConditionEntries]; private bool isDisposed; @@ -38,7 +38,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition this.framework.Update += this.FrameworkUpdate; } - + /// Finalizes an instance of the class. ~Condition() => this.Dispose(false); @@ -69,12 +69,12 @@ internal sealed class Condition : IInternalDisposableService, ICondition /// void IInternalDisposableService.DisposeService() => this.Dispose(true); - + /// public IReadOnlySet AsReadOnlySet() { var result = new HashSet(); - + for (var i = 0; i < MaxConditionEntries; i++) { if (this[i]) @@ -99,14 +99,14 @@ internal sealed class Condition : IInternalDisposableService, ICondition return false; } - + /// public bool Any(params ConditionFlag[] flags) { foreach (var flag in flags) { // this[i] performs range checking, so no need to check here - if (this[flag]) + if (this[flag]) { return true; } @@ -114,7 +114,7 @@ internal sealed class Condition : IInternalDisposableService, ICondition return false; } - + /// public bool AnyExcept(params ConditionFlag[] excluded) { @@ -157,13 +157,16 @@ internal sealed class Condition : IInternalDisposableService, ICondition { this.cache[i] = value; - try + foreach (var d in Delegate.EnumerateInvocationList(this.ConditionChange)) { - this.ConditionChange?.Invoke((ConditionFlag)i, value); - } - catch (Exception ex) - { - Log.Error(ex, $"While invoking {nameof(this.ConditionChange)}, an exception was thrown."); + try + { + d((ConditionFlag)i, value); + } + catch (Exception ex) + { + Log.Error(ex, $"While invoking {d.Method.Name}, an exception was thrown."); + } } } } @@ -190,7 +193,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition { this.conditionService.ConditionChange += this.ConditionChangedForward; } - + /// public event ICondition.ConditionChangeDelegate? ConditionChange; @@ -202,7 +205,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition /// public bool this[int flag] => this.conditionService[flag]; - + /// void IInternalDisposableService.DisposeService() { @@ -210,7 +213,7 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition this.ConditionChange = null; } - + /// public IReadOnlySet AsReadOnlySet() => this.conditionService.AsReadOnlySet(); @@ -222,10 +225,10 @@ internal class ConditionPluginScoped : IInternalDisposableService, ICondition /// public bool AnyExcept(params ConditionFlag[] except) => this.conditionService.AnyExcept(except); - + /// public bool OnlyAny(params ConditionFlag[] other) => this.conditionService.OnlyAny(other); - + /// public bool EqualTo(params ConditionFlag[] other) => this.conditionService.EqualTo(other); diff --git a/Dalamud/Game/Command/CommandManager.cs b/Dalamud/Game/Command/CommandManager.cs index b72238abe..01442c409 100644 --- a/Dalamud/Game/Command/CommandManager.cs +++ b/Dalamud/Game/Command/CommandManager.cs @@ -132,7 +132,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma return false; } - this.CommandAdded?.Invoke(this, new CommandEventArgs + this.CommandAdded?.InvokeSafely(this, new CommandEventArgs { Command = command, CommandInfo = info, @@ -160,7 +160,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma return false; } - this.CommandAdded?.Invoke(this, new CommandEventArgs + this.CommandAdded?.InvokeSafely(this, new CommandEventArgs { Command = command, CommandInfo = info, @@ -180,7 +180,7 @@ internal sealed unsafe class CommandManager : IInternalDisposableService, IComma var removed = this.commandMap.Remove(command, out var info); if (removed) { - this.CommandRemoved?.Invoke(this, new CommandEventArgs + this.CommandRemoved?.InvokeSafely(this, new CommandEventArgs { Command = command, CommandInfo = info, diff --git a/Dalamud/Game/Framework.cs b/Dalamud/Game/Framework.cs index 88f9d0bb6..808bbce50 100644 --- a/Dalamud/Game/Framework.cs +++ b/Dalamud/Game/Framework.cs @@ -350,17 +350,13 @@ internal sealed class Framework : IInternalDisposableService, IFramework /// The Framework Instance to pass to delegate. internal void ProfileAndInvoke(IFramework.OnUpdateDelegate? eventDelegate, IFramework frameworkInstance) { - if (eventDelegate is null) return; - - var invokeList = eventDelegate.GetInvocationList(); - // Individually invoke OnUpdate handlers and time them. - foreach (var d in invokeList) + foreach (var d in Delegate.EnumerateInvocationList(eventDelegate)) { var stopwatch = Stopwatch.StartNew(); try { - d.Method.Invoke(d.Target, new object[] { frameworkInstance }); + d(frameworkInstance); } catch (Exception ex) { @@ -370,8 +366,7 @@ internal sealed class Framework : IInternalDisposableService, IFramework stopwatch.Stop(); var key = $"{d.Target}::{d.Method.Name}"; - if (this.NonUpdatedSubDelegates.Contains(key)) - this.NonUpdatedSubDelegates.Remove(key); + this.NonUpdatedSubDelegates.Remove(key); AddToStats(key, stopwatch.Elapsed.TotalMilliseconds); } diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index 721070d9b..022968c7e 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -346,24 +346,21 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui // Call events var isHandled = false; - if (this.CheckMessageHandled is { } handledCallback) + foreach (var action in Delegate.EnumerateInvocationList(this.CheckMessageHandled)) { - foreach (var action in handledCallback.GetInvocationList().Cast()) + try { - try - { - action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); - } - catch (Exception e) - { - Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method); - } + action(chatType, timestamp, ref parsedSender, ref parsedMessage, ref isHandled); + } + catch (Exception e) + { + Log.Error(e, "Could not invoke registered OnCheckMessageHandledDelegate for {Name}", action.Method); } } - if (!isHandled && this.ChatMessage is { } callback) + if (!isHandled) { - foreach (var action in callback.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.ChatMessage)) { try { @@ -394,12 +391,14 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui // Print the original chat if it's handled. if (isHandled) { - this.ChatMessageHandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage); + foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageHandled)) + d(chatType, timestamp, parsedSender, parsedMessage); } else { messageId = this.printMessageHook.Original(manager, chatType, sender, message, timestamp, silent); - this.ChatMessageUnhandled?.Invoke(chatType, timestamp, parsedSender, parsedMessage); + foreach (var d in Delegate.EnumerateInvocationList(this.ChatMessageUnhandled)) + d(chatType, timestamp, parsedSender, parsedMessage); } } catch (Exception ex) diff --git a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs index cbf166cfc..d869c6fd1 100644 --- a/Dalamud/Game/Gui/FlyText/FlyTextGui.cs +++ b/Dalamud/Game/Gui/FlyText/FlyTextGui.cs @@ -75,7 +75,7 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui strArray->SetValue((int)strOffset + 0, text1.EncodeWithNullTerminator(), false, true, false); strArray->SetValue((int)strOffset + 1, text2.EncodeWithNullTerminator(), false, true, false); - + flytext->AddFlyText(actorIndex, 1, numArray, numOffset, 10, strArray, strOffset, 2, 0); } @@ -116,17 +116,27 @@ internal sealed class FlyTextGui : IInternalDisposableService, IFlyTextGui $"text1({(nint)text1:X}, \"{tmpText1}\") text2({(nint)text2:X}, \"{tmpText2}\") " + $"color({color:X}) icon({icon}) yOffset({yOffset})"); Log.Verbose("[FlyText] Calling flytext events!"); - this.FlyTextCreated?.Invoke( - ref tmpKind, - ref tmpVal1, - ref tmpVal2, - ref tmpText1, - ref tmpText2, - ref tmpColor, - ref tmpIcon, - ref tmpDamageTypeIcon, - ref tmpYOffset, - ref handled); + foreach (var d in Delegate.EnumerateInvocationList(this.FlyTextCreated)) + { + try + { + d( + ref tmpKind, + ref tmpVal1, + ref tmpVal2, + ref tmpText1, + ref tmpText2, + ref tmpColor, + ref tmpIcon, + ref tmpDamageTypeIcon, + ref tmpYOffset, + ref handled); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } // If handled, ignore the original call if (handled) diff --git a/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs b/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs index 32192ad21..cd84b996b 100644 --- a/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs +++ b/Dalamud/Game/Gui/NamePlate/NamePlateGui.cs @@ -6,6 +6,7 @@ using Dalamud.Hooking; using Dalamud.IoC; using Dalamud.IoC.Internal; using Dalamud.Plugin.Services; +using Dalamud.Utility; using FFXIVClientStructs.FFXIV.Client.UI; using FFXIVClientStructs.FFXIV.Component.GUI; @@ -169,8 +170,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui handler.ResetState(); } - this.OnDataUpdate?.Invoke(this.context, activeHandlers); - this.OnNamePlateUpdate?.Invoke(this.context, activeHandlers); + this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers); + this.OnNamePlateUpdate?.InvokeSafely(this.context, activeHandlers); if (this.context.HasParts) this.ApplyBuilders(activeHandlers); @@ -185,8 +186,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); } - this.OnPostNamePlateUpdate?.Invoke(this.context, activeHandlers); - this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); + this.OnPostNamePlateUpdate?.InvokeSafely(this.context, activeHandlers); + this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers); } else { @@ -200,8 +201,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui if (this.OnDataUpdate is not null) { - this.OnDataUpdate?.Invoke(this.context, activeHandlers); - this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers); + this.OnDataUpdate?.InvokeSafely(this.context, activeHandlers); + this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers); if (this.context.HasParts) this.ApplyBuilders(activeHandlers); @@ -216,12 +217,12 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); } - this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers); - this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); + this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers); + this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers); } else if (updatedHandlers.Count != 0) { - this.OnNamePlateUpdate?.Invoke(this.context, updatedHandlers); + this.OnNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers); if (this.context.HasParts) this.ApplyBuilders(updatedHandlers); @@ -236,8 +237,8 @@ internal sealed class NamePlateGui : IInternalDisposableService, INamePlateGui Log.Error(e, "Caught exception when calling original AddonNamePlate OnRequestedUpdate."); } - this.OnPostNamePlateUpdate?.Invoke(this.context, updatedHandlers); - this.OnPostDataUpdate?.Invoke(this.context, activeHandlers); + this.OnPostNamePlateUpdate?.InvokeSafely(this.context, updatedHandlers); + this.OnPostDataUpdate?.InvokeSafely(this.context, activeHandlers); } } } diff --git a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs index 0b25a87be..5caadb29d 100644 --- a/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs +++ b/Dalamud/Game/Gui/PartyFinder/PartyFinderGui.cs @@ -89,7 +89,17 @@ internal sealed unsafe class PartyFinderGui : IInternalDisposableService, IParty var listing = new PartyFinderListing(packet.Listings[i]); var args = new PartyFinderListingEventArgs(packet.BatchNumber); - this.ReceiveListing?.Invoke(listing, args); + foreach (var d in Delegate.EnumerateInvocationList(this.ReceiveListing)) + { + try + { + d(listing, args); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } if (args.Visible) { diff --git a/Dalamud/Game/Gui/Toast/ToastGui.cs b/Dalamud/Game/Gui/Toast/ToastGui.cs index 6b55b3408..ab0b05445 100644 --- a/Dalamud/Game/Gui/Toast/ToastGui.cs +++ b/Dalamud/Game/Gui/Toast/ToastGui.cs @@ -10,6 +10,8 @@ using Dalamud.Plugin.Services; using FFXIVClientStructs.FFXIV.Client.UI; +using Serilog; + namespace Dalamud.Game.Gui.Toast; /// @@ -112,7 +114,7 @@ internal sealed partial class ToastGui options ??= new ToastOptions(); this.normalQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); } - + /// public void ShowNormal(SeString message, ToastOptions? options = null) { @@ -150,7 +152,17 @@ internal sealed partial class ToastGui Speed = (ToastSpeed)isFast, }; - this.Toast?.Invoke(ref str, ref options, ref isHandled); + foreach (var d in Delegate.EnumerateInvocationList(this.Toast)) + { + try + { + d.Invoke(ref str, ref options, ref isHandled); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } // do nothing if handled if (isHandled) @@ -180,7 +192,7 @@ internal sealed partial class ToastGui options ??= new QuestToastOptions(); this.questQueue.Enqueue((Encoding.UTF8.GetBytes(message), options)); } - + /// public void ShowQuest(SeString message, QuestToastOptions? options = null) { @@ -223,7 +235,17 @@ internal sealed partial class ToastGui PlaySound = playSound == 1, }; - this.QuestToast?.Invoke(ref str, ref options, ref isHandled); + foreach (var d in Delegate.EnumerateInvocationList(this.QuestToast)) + { + try + { + d.Invoke(ref str, ref options, ref isHandled); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } // do nothing if handled if (isHandled) @@ -286,7 +308,17 @@ internal sealed partial class ToastGui var isHandled = false; var str = SeString.Parse(text); - this.ErrorToast?.Invoke(ref str, ref isHandled); + foreach (var d in Delegate.EnumerateInvocationList(this.ErrorToast)) + { + try + { + d.Invoke(ref str, ref isHandled); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } // do nothing if handled if (isHandled) @@ -321,16 +353,16 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui this.toastGuiService.QuestToast += this.QuestToastForward; this.toastGuiService.ErrorToast += this.ErrorToastForward; } - + /// public event IToastGui.OnNormalToastDelegate? Toast; - + /// public event IToastGui.OnQuestToastDelegate? QuestToast; - + /// public event IToastGui.OnErrorToastDelegate? ErrorToast; - + /// void IInternalDisposableService.DisposeService() { @@ -342,7 +374,7 @@ internal class ToastGuiPluginScoped : IInternalDisposableService, IToastGui this.QuestToast = null; this.ErrorToast = null; } - + /// public void ShowNormal(string message, ToastOptions? options = null) => this.toastGuiService.ShowNormal(message, options); diff --git a/Dalamud/Game/Marketboard/MarketBoard.cs b/Dalamud/Game/Marketboard/MarketBoard.cs index 79de09dea..962e0010e 100644 --- a/Dalamud/Game/Marketboard/MarketBoard.cs +++ b/Dalamud/Game/Marketboard/MarketBoard.cs @@ -150,9 +150,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar private void OnHistoryReceived(IMarketBoardHistory history) { - if (this.HistoryReceived == null) return; - - foreach (var action in this.HistoryReceived.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.HistoryReceived)) { try { @@ -167,9 +165,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar private void OnItemPurchased(IMarketBoardPurchase purchase) { - if (this.ItemPurchased == null) return; - - foreach (var action in this.ItemPurchased.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.ItemPurchased)) { try { @@ -184,10 +180,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar private void OnOfferingsReceived(IMarketBoardCurrentOfferings currentOfferings) { - if (this.OfferingsReceived == null) return; - - foreach (var action in this.OfferingsReceived.GetInvocationList() - .Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.OfferingsReceived)) { try { @@ -202,9 +195,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar private void OnPurchaseRequested(IMarketBoardPurchaseHandler purchaseHandler) { - if (this.PurchaseRequested == null) return; - - foreach (var action in this.PurchaseRequested.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.PurchaseRequested)) { try { @@ -219,9 +210,7 @@ internal class MarketBoardPluginScoped : IInternalDisposableService, IMarketBoar private void OnTaxRatesReceived(IMarketTaxRates taxRates) { - if (this.TaxRatesReceived == null) return; - - foreach (var action in this.TaxRatesReceived.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(this.TaxRatesReceived)) { try { diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs index 5022eb93d..5c21add0f 100644 --- a/Dalamud/Game/Network/GameNetwork.cs +++ b/Dalamud/Game/Network/GameNetwork.cs @@ -28,7 +28,7 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw [ServiceManager.ServiceDependency] private readonly DalamudConfiguration configuration = Service.Get(); - + [ServiceManager.ServiceConstructor] private unsafe GameNetwork(TargetSigScanner sigScanner) { @@ -71,32 +71,36 @@ internal sealed unsafe class GameNetwork : IInternalDisposableService, IGameNetw // Go back 0x10 to get back to the start of the packet header dataPtr -= 0x10; - try + foreach (var d in Delegate.EnumerateInvocationList(this.NetworkMessage)) { - // Call events - this.NetworkMessage?.Invoke(dataPtr + 0x20, (ushort)Marshal.ReadInt16(dataPtr, 0x12), 0, targetId, NetworkMessageDirection.ZoneDown); - - this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); - } - catch (Exception ex) - { - string header; try { - var data = new byte[32]; - Marshal.Copy(dataPtr, data, 0, 32); - header = BitConverter.ToString(data); + d.Invoke( + dataPtr + 0x20, + (ushort)Marshal.ReadInt16(dataPtr, 0x12), + 0, + targetId, + NetworkMessageDirection.ZoneDown); } - catch (Exception) + catch (Exception ex) { - header = "failed"; + string header; + try + { + var data = new byte[32]; + Marshal.Copy(dataPtr, data, 0, 32); + header = BitConverter.ToString(data); + } + catch (Exception) + { + header = "failed"; + } + + Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); } - - Log.Error(ex, "Exception on ProcessZonePacketDown hook. Header: " + header); - - this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); } + this.processZonePacketDownHook.Original(dispatcher, targetId, dataPtr + 0x10); this.hitchDetectorDown.Stop(); } @@ -153,7 +157,7 @@ internal class GameNetworkPluginScoped : IInternalDisposableService, IGameNetwor { this.gameNetworkService.NetworkMessage += this.NetworkMessageForward; } - + /// public event IGameNetwork.OnNetworkMessageDelegate? NetworkMessage; diff --git a/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs b/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs index e8e0b7536..7c70a9f0b 100644 --- a/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs +++ b/Dalamud/Hooking/WndProcHook/WndProcHookManager.cs @@ -36,7 +36,7 @@ internal sealed class WndProcHookManager : IInternalDisposableService this.dispatchMessageWHook.Enable(); // Capture the game main window handle, - // so that no guarantees would have to be made on the service dispose order. + // so that no guarantees would have to be made on the service dispose order. Service .GetAsync() .ContinueWith(r => this.mainWindowHwnd = (HWND)r.Result.Manager.GameWindowHandle); @@ -82,13 +82,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService /// The arguments. internal void InvokePreWndProc(WndProcEventArgs args) { - try + foreach (var d in Delegate.EnumerateInvocationList(this.PreWndProc)) { - this.PreWndProc?.Invoke(args); - } - catch (Exception e) - { - Log.Error(e, $"{nameof(this.PreWndProc)} error"); + try + { + d(args); + } + catch (Exception e) + { + Log.Error(e, $"{nameof(this.PreWndProc)} error calling {d.Method.Name}"); + } } } @@ -98,13 +101,16 @@ internal sealed class WndProcHookManager : IInternalDisposableService /// The arguments. internal void InvokePostWndProc(WndProcEventArgs args) { - try + foreach (var d in Delegate.EnumerateInvocationList(this.PostWndProc)) { - this.PostWndProc?.Invoke(args); - } - catch (Exception e) - { - Log.Error(e, $"{nameof(this.PostWndProc)} error"); + try + { + d(args); + } + catch (Exception e) + { + Log.Error(e, $"{nameof(this.PostWndProc)} error calling {d.Method.Name}"); + } } } diff --git a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs index c3135853d..77a4925f8 100644 --- a/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs +++ b/Dalamud/Interface/ImGuiNotification/Internal/ActiveNotification.cs @@ -278,7 +278,7 @@ internal sealed partial class ActiveNotification : IActiveNotification if (@delegate is null) return null; - foreach (var il in @delegate.GetInvocationList()) + foreach (var il in Delegate.EnumerateInvocationList(@delegate)) { if (il.Target is { } target && !IsOwnedByDalamud(target.GetType())) @delegate = (T)Delegate.Remove(@delegate, il); diff --git a/Dalamud/Interface/ImGuiNotification/Internal/NotificationPositionChooser.cs b/Dalamud/Interface/ImGuiNotification/Internal/NotificationPositionChooser.cs index 2a50c5ae8..9cda48145 100644 --- a/Dalamud/Interface/ImGuiNotification/Internal/NotificationPositionChooser.cs +++ b/Dalamud/Interface/ImGuiNotification/Internal/NotificationPositionChooser.cs @@ -3,6 +3,7 @@ using Dalamud.Configuration.Internal; using Dalamud.Interface.Utility; using Dalamud.Interface.Utility.Raii; +using Dalamud.Utility; using ImGuiNET; @@ -69,14 +70,14 @@ internal class NotificationPositionChooser if (ImGui.IsMouseClicked(ImGuiMouseButton.Right)) { - this.SelectionMade?.Invoke(); + this.SelectionMade.InvokeSafely(); } else if (ImGui.IsMouseClicked(ImGuiMouseButton.Left)) { this.configuration.NotificationAnchorPosition = this.currentAnchorPosition; this.configuration.QueueSave(); - this.SelectionMade?.Invoke(); + this.SelectionMade.InvokeSafely(); } // In the middle of the screen, draw some instructions diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs index ec175c5aa..ed6fffb4e 100644 --- a/Dalamud/Interface/Internal/InterfaceManager.cs +++ b/Dalamud/Interface/Internal/InterfaceManager.cs @@ -832,7 +832,7 @@ internal partial class InterfaceManager : IInternalDisposableService this.defaultFontResourceLock = fontLocked; // Broadcast to auto-rebuilding instances. - this.AfterBuildFonts?.Invoke(); + this.AfterBuildFonts.InvokeSafely(); }); }; } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs index 52939385b..39e324ff2 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/DelegateFontHandle.cs @@ -66,7 +66,7 @@ internal sealed class DelegateFontHandle : FontHandle var key = new DelegateFontHandle(this, buildStepDelegate); lock (this.syncRoot) this.handles.Add(key); - this.RebuildRecommend?.Invoke(); + this.RebuildRecommend.InvokeSafely(); return key; } @@ -259,7 +259,7 @@ internal sealed class DelegateFontHandle : FontHandle } } - /// + /// public void OnPreBuildCleanup(IFontAtlasBuildToolkitPreBuild toolkitPreBuild) { // irrelevant diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs index 61ac00faf..2d3b9582c 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontAtlasFactory.Implementation.cs @@ -386,7 +386,7 @@ internal sealed partial class FontAtlasFactory if (this.disposed) return; - this.BeforeDispose?.InvokeSafely(this); + this.BeforeDispose.InvokeSafely(this); try { @@ -400,25 +400,11 @@ internal sealed partial class FontAtlasFactory this.disposables.Dispose(); } - try - { - this.AfterDispose?.Invoke(this, null); - } - catch - { - // ignore - } + this.AfterDispose.InvokeSafely(this, null); } catch (Exception e) { - try - { - this.AfterDispose?.Invoke(this, e); - } - catch - { - // ignore - } + this.AfterDispose.InvokeSafely(this, e); } GC.SuppressFinalize(this); @@ -664,7 +650,7 @@ internal sealed partial class FontAtlasFactory { res = new(this, scale); foreach (var fhm in this.fontHandleManagers) - res.InitialAddSubstance(fhm.NewSubstance(res)); + res.InitialAddSubstance(fhm.NewSubstance(res)); unsafe { atlasPtr = (nint)res.Atlas.NativePtr; @@ -699,7 +685,7 @@ internal sealed partial class FontAtlasFactory res = new(this, scale); foreach (var fhm in this.fontHandleManagers) - res.InitialAddSubstance(fhm.NewSubstance(res)); + res.InitialAddSubstance(fhm.NewSubstance(res)); unsafe { atlasPtr = (nint)res.Atlas.NativePtr; @@ -828,7 +814,7 @@ internal sealed partial class FontAtlasFactory this.factory.Framework.RunOnFrameworkThread( () => { - this.RebuildRecommend?.InvokeSafely(); + this.RebuildRecommend.InvokeSafely(); switch (this.AutoRebuildMode) { diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs index b84a857da..7f390350c 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/FontHandle.cs @@ -79,13 +79,16 @@ internal abstract class FontHandle : IFontHandle /// The font, locked during the call of . public void InvokeImFontChanged(ILockedImFont font) { - try + foreach (var d in Delegate.EnumerateInvocationList(this.ImFontChanged)) { - this.ImFontChanged?.Invoke(this, font); - } - catch (Exception e) - { - Log.Error(e, $"{nameof(this.InvokeImFontChanged)}: error"); + try + { + d(this, font); + } + catch (Exception e) + { + Log.Error(e, $"{nameof(this.InvokeImFontChanged)}: error calling {d.Method.Name}"); + } } } diff --git a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs index 636bee2e2..3d1dfa674 100644 --- a/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs +++ b/Dalamud/Interface/ManagedFontAtlas/Internals/GamePrebakedFontHandle.cs @@ -151,7 +151,7 @@ internal class GamePrebakedFontHandle : FontHandle } if (suggestRebuild) - this.RebuildRecommend?.Invoke(); + this.RebuildRecommend.InvokeSafely(); return handle; } @@ -641,7 +641,7 @@ internal class GamePrebakedFontHandle : FontHandle glyphIndex = (ushort)glyphs.Length; glyphs.Add(default); } - + ref var g = ref glyphs[glyphIndex]; g = sourceGlyph; if (fontScaleMode == FontScaleMode.SkipHandling) @@ -807,7 +807,7 @@ internal class GamePrebakedFontHandle : FontHandle .GetCustomRectByIndex(rectId) .NativePtr; var widthAdjustment = this.BaseStyle.CalculateBaseWidthAdjustment(fdtFontHeader, fdtGlyph); - + // Glyph is scaled at this point; undo that. ref var glyph = ref glyphs[lookups[rc.GlyphId]]; glyph.X0 = this.BaseAttr.HorizontalOffset; @@ -822,7 +822,7 @@ internal class GamePrebakedFontHandle : FontHandle this.gftp.GetTexFile(this.BaseAttr.TexPathFormat, fdtGlyph.TextureFileIndex); var sourceBuffer = texFiles[fdtGlyph.TextureFileIndex].ImageData; var sourceBufferDelta = fdtGlyph.TextureChannelByteIndex; - + for (var y = 0; y < fdtGlyph.BoundingHeight; y++) { var sourcePixelIndex = @@ -830,11 +830,11 @@ internal class GamePrebakedFontHandle : FontHandle sourcePixelIndex *= 4; sourcePixelIndex += sourceBufferDelta; var blend1 = horzBlend[fdtGlyph.CurrentOffsetY + y]; - + var targetOffset = ((rc.Y + y) * width) + rc.X; for (var x = 0; x < rc.Width; x++) pixels8[targetOffset + x] = 0; - + targetOffset += horzShift[fdtGlyph.CurrentOffsetY + y]; if (blend1 == 0) { diff --git a/Dalamud/Localization.cs b/Dalamud/Localization.cs index 84e8437b3..1e72c3075 100644 --- a/Dalamud/Localization.cs +++ b/Dalamud/Localization.cs @@ -117,7 +117,18 @@ public class Localization : IServiceType public void SetupWithFallbacks() { this.DalamudLanguageCultureInfo = CultureInfo.InvariantCulture; - this.LocalizationChanged?.Invoke(FallbackLangCode); + foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged)) + { + try + { + d(FallbackLangCode); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } + Loc.SetupWithFallbacks(this.assembly); } @@ -134,7 +145,17 @@ public class Localization : IServiceType } this.DalamudLanguageCultureInfo = GetCultureInfoFromLangCode(langCode); - this.LocalizationChanged?.Invoke(langCode); + foreach (var d in Delegate.EnumerateInvocationList(this.LocalizationChanged)) + { + try + { + d(langCode); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", d.Method); + } + } try { diff --git a/Dalamud/Plugin/DalamudPluginInterface.cs b/Dalamud/Plugin/DalamudPluginInterface.cs index f82d241d4..e1c6b7830 100644 --- a/Dalamud/Plugin/DalamudPluginInterface.cs +++ b/Dalamud/Plugin/DalamudPluginInterface.cs @@ -527,18 +527,15 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa /// If this plugin was affected by the change. internal void NotifyActivePluginsChanged(PluginListInvalidationKind kind, bool affectedThisPlugin) { - if (this.ActivePluginsChanged is { } callback) + foreach (var action in Delegate.EnumerateInvocationList(this.ActivePluginsChanged)) { - foreach (var action in callback.GetInvocationList().Cast()) + try { - try - { - action(kind, affectedThisPlugin); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); - } + action(kind, affectedThisPlugin); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); } } } @@ -547,18 +544,15 @@ internal sealed class DalamudPluginInterface : IDalamudPluginInterface, IDisposa { this.UiLanguage = langCode; - if (this.LanguageChanged is { } callback) + foreach (var action in Delegate.EnumerateInvocationList(this.LanguageChanged)) { - foreach (var action in callback.GetInvocationList().Cast()) + try { - try - { - action(langCode); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", action.Method); - } + action(langCode); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); } } } diff --git a/Dalamud/Utility/EventHandlerExtensions.cs b/Dalamud/Utility/EventHandlerExtensions.cs index 9bb35a8f1..285e18fa2 100644 --- a/Dalamud/Utility/EventHandlerExtensions.cs +++ b/Dalamud/Utility/EventHandlerExtensions.cs @@ -1,7 +1,8 @@ -using System.Linq; +using System.Collections.Generic; using Dalamud.Game; using Dalamud.Game.Gui.ContextMenu; +using Dalamud.Game.Gui.NamePlate; using Dalamud.Plugin.Services; using Serilog; @@ -14,25 +15,29 @@ internal static class EventHandlerExtensions { /// /// Replacement for Invoke() on EventHandlers to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. + /// of a thrown Exception inside an invocation. /// /// The EventHandler in question. /// Default sender for Invoke equivalent. /// Default EventArgs for Invoke equivalent. public static void InvokeSafely(this EventHandler? eh, object sender, EventArgs e) { - if (eh == null) - return; - - foreach (var handler in eh.GetInvocationList().Cast()) + foreach (var handler in Delegate.EnumerateInvocationList(eh)) { - HandleInvoke(() => handler(sender, e)); + try + { + handler(sender, e); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", handler.Method); + } } } /// /// Replacement for Invoke() on generic EventHandlers to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. + /// of a thrown Exception inside an invocation. /// /// The EventHandler in question. /// Default sender for Invoke equivalent. @@ -40,104 +45,135 @@ internal static class EventHandlerExtensions /// Type of EventArgs. public static void InvokeSafely(this EventHandler? eh, object sender, T e) { - if (eh == null) - return; - - foreach (var handler in eh.GetInvocationList().Cast>()) + foreach (var handler in Delegate.EnumerateInvocationList(eh)) { - HandleInvoke(() => handler(sender, e)); + try + { + handler(sender, e); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", handler.Method); + } } } /// /// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. + /// of a thrown Exception inside an invocation. /// /// The Action in question. public static void InvokeSafely(this Action? act) { - if (act == null) - return; - - foreach (var action in act.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(act)) { - HandleInvoke(action); + try + { + action(); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } - /// - /// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. - /// - /// The Action in question. - /// Templated argument for Action. - /// Type of Action args. + /// public static void InvokeSafely(this Action? act, T argument) { - if (act == null) - return; - - foreach (var action in act.GetInvocationList().Cast>()) + foreach (var action in Delegate.EnumerateInvocationList(act)) { - HandleInvoke(action, argument); + try + { + action(argument); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } + } + } + + /// + public static void InvokeSafely(this Action? act, T1 arg1, T2 arg2) + { + foreach (var action in Delegate.EnumerateInvocationList(act)) + { + try + { + action(arg1, arg2); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } /// /// Replacement for Invoke() on OnUpdateDelegate to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. + /// of a thrown Exception inside an invocation. /// /// The OnUpdateDelegate in question. /// Framework to be passed on to OnUpdateDelegate. public static void InvokeSafely(this IFramework.OnUpdateDelegate? updateDelegate, Framework framework) { - if (updateDelegate == null) - return; - - foreach (var action in updateDelegate.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(updateDelegate)) { - HandleInvoke(() => action(framework)); + try + { + action(framework); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } /// /// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case - /// of a thrown Exception inside of an invocation. + /// of a thrown Exception inside an invocation. /// /// The OnMenuOpenedDelegate in question. /// Templated argument for Action. public static void InvokeSafely(this IContextMenu.OnMenuOpenedDelegate? openedDelegate, MenuOpenedArgs argument) { - if (openedDelegate == null) - return; - - foreach (var action in openedDelegate.GetInvocationList().Cast()) + foreach (var action in Delegate.EnumerateInvocationList(openedDelegate)) { - HandleInvoke(() => action(argument)); + try + { + action(argument); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } - private static void HandleInvoke(Action act) + /// + /// Replacement for Invoke() on OnMenuOpenedDelegate to catch exceptions that stop event propagation in case + /// of a thrown Exception inside an invocation. + /// + /// The OnMenuOpenedDelegate in question. + /// An object containing information about the pending data update. + /// A list of handlers used for updating nameplate data. + public static void InvokeSafely( + this INamePlateGui.OnPlateUpdateDelegate? updatedDelegate, + INamePlateUpdateContext context, + IReadOnlyList handlers) { - try + foreach (var action in Delegate.EnumerateInvocationList(updatedDelegate)) { - act(); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", act.Method); - } - } - - private static void HandleInvoke(Action act, T argument) - { - try - { - act(argument); - } - catch (Exception ex) - { - Log.Error(ex, "Exception during raise of {handler}", act.Method); + try + { + action(context, handlers); + } + catch (Exception ex) + { + Log.Error(ex, "Exception during raise of {handler}", action.Method); + } } } }