mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 10:17:22 +01:00
add exception handling for some event delegates
This commit is contained in:
parent
805133c9fe
commit
652b357341
8 changed files with 166 additions and 100 deletions
|
|
@ -9,6 +9,7 @@ using Dalamud.Game.Network.Internal;
|
|||
using Dalamud.Hooking;
|
||||
using Dalamud.IoC;
|
||||
using Dalamud.IoC.Internal;
|
||||
using Dalamud.Utility;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Serilog;
|
||||
|
||||
|
|
@ -145,7 +146,7 @@ namespace Dalamud.Game.ClientState
|
|||
private IntPtr SetupTerritoryTypeDetour(IntPtr manager, ushort terriType)
|
||||
{
|
||||
this.TerritoryType = terriType;
|
||||
this.TerritoryChanged?.Invoke(this, terriType);
|
||||
this.TerritoryChanged?.Raise(this, terriType);
|
||||
|
||||
Log.Debug("TerritoryType changed: {0}", terriType);
|
||||
|
||||
|
|
@ -154,7 +155,7 @@ namespace Dalamud.Game.ClientState
|
|||
|
||||
private void NetworkHandlersOnCfPop(object sender, Lumina.Excel.GeneratedSheets.ContentFinderCondition e)
|
||||
{
|
||||
this.CfPop?.Invoke(this, e);
|
||||
this.CfPop?.Raise(this, e);
|
||||
}
|
||||
|
||||
private void FrameworkOnOnUpdateEvent(Framework framework1)
|
||||
|
|
@ -171,7 +172,7 @@ namespace Dalamud.Game.ClientState
|
|||
Log.Debug("Is login");
|
||||
this.lastConditionNone = false;
|
||||
this.IsLoggedIn = true;
|
||||
this.Login?.Invoke(this, null);
|
||||
this.Login?.Raise(this, null);
|
||||
gameGui.ResetUiHideState();
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +181,7 @@ namespace Dalamud.Game.ClientState
|
|||
Log.Debug("Is logout");
|
||||
this.lastConditionNone = true;
|
||||
this.IsLoggedIn = false;
|
||||
this.Logout?.Invoke(this, null);
|
||||
this.Logout?.Raise(this, null);
|
||||
gameGui.ResetUiHideState();
|
||||
}
|
||||
|
||||
|
|
@ -193,11 +194,11 @@ namespace Dalamud.Game.ClientState
|
|||
|
||||
if (this.IsPvP)
|
||||
{
|
||||
this.EnterPvP?.Invoke();
|
||||
this.EnterPvP?.Raise();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LeavePvP?.Invoke();
|
||||
this.LeavePvP?.Raise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -362,63 +362,64 @@ namespace Dalamud.Game
|
|||
this.LastUpdate = DateTime.Now;
|
||||
this.LastUpdateUTC = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
this.runOnNextTickTaskList.RemoveAll(x => x.Run());
|
||||
|
||||
if (StatsEnabled && this.Update != null)
|
||||
{
|
||||
this.runOnNextTickTaskList.RemoveAll(x => x.Run());
|
||||
// Stat Tracking for Framework Updates
|
||||
var invokeList = this.Update.GetInvocationList();
|
||||
var notUpdated = StatsHistory.Keys.ToList();
|
||||
|
||||
if (StatsEnabled && this.Update != null)
|
||||
// Individually invoke OnUpdate handlers and time them.
|
||||
foreach (var d in invokeList)
|
||||
{
|
||||
// Stat Tracking for Framework Updates
|
||||
var invokeList = this.Update.GetInvocationList();
|
||||
var notUpdated = StatsHistory.Keys.ToList();
|
||||
|
||||
// Individually invoke OnUpdate handlers and time them.
|
||||
foreach (var d in invokeList)
|
||||
statsStopwatch.Restart();
|
||||
try
|
||||
{
|
||||
statsStopwatch.Restart();
|
||||
d.Method.Invoke(d.Target, new object[] { this });
|
||||
statsStopwatch.Stop();
|
||||
|
||||
var key = $"{d.Target}::{d.Method.Name}";
|
||||
if (notUpdated.Contains(key))
|
||||
notUpdated.Remove(key);
|
||||
|
||||
if (!StatsHistory.ContainsKey(key))
|
||||
StatsHistory.Add(key, new List<double>());
|
||||
|
||||
StatsHistory[key].Add(statsStopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
if (StatsHistory[key].Count > 1000)
|
||||
{
|
||||
StatsHistory[key].RemoveRange(0, StatsHistory[key].Count - 1000);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
||||
}
|
||||
|
||||
// Cleanup handlers that are no longer being called
|
||||
foreach (var key in notUpdated)
|
||||
statsStopwatch.Stop();
|
||||
|
||||
var key = $"{d.Target}::{d.Method.Name}";
|
||||
if (notUpdated.Contains(key))
|
||||
notUpdated.Remove(key);
|
||||
|
||||
if (!StatsHistory.ContainsKey(key))
|
||||
StatsHistory.Add(key, new List<double>());
|
||||
|
||||
StatsHistory[key].Add(statsStopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
if (StatsHistory[key].Count > 1000)
|
||||
{
|
||||
if (StatsHistory[key].Count > 0)
|
||||
{
|
||||
StatsHistory[key].RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsHistory.Remove(key);
|
||||
}
|
||||
StatsHistory[key].RemoveRange(0, StatsHistory[key].Count - 1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// Cleanup handlers that are no longer being called
|
||||
foreach (var key in notUpdated)
|
||||
{
|
||||
this.Update?.Invoke(this);
|
||||
if (StatsHistory[key].Count > 0)
|
||||
{
|
||||
StatsHistory[key].RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsHistory.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
else
|
||||
{
|
||||
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
||||
this.Update?.Raise(this);
|
||||
}
|
||||
}
|
||||
|
||||
original:
|
||||
original:
|
||||
return this.updateHook.OriginalDisposeSafe(framework);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -466,14 +466,7 @@ namespace Dalamud.Game.Gui
|
|||
var itemId = (ulong)Marshal.ReadInt32(hoverState, 0x138);
|
||||
this.HoveredItem = itemId;
|
||||
|
||||
try
|
||||
{
|
||||
this.HoveredItemChanged?.Invoke(this, itemId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not dispatch HoveredItemChanged event.");
|
||||
}
|
||||
this.HoveredItemChanged?.Raise(this, itemId);
|
||||
|
||||
Log.Verbose("HoverItemId:{0} this:{1}", itemId, hoverState.ToInt64().ToString("X"));
|
||||
}
|
||||
|
|
@ -515,14 +508,7 @@ namespace Dalamud.Game.Gui
|
|||
this.HoveredAction.ActionKind = actionKind;
|
||||
this.HoveredAction.BaseActionID = actionId;
|
||||
this.HoveredAction.ActionID = (uint)Marshal.ReadInt32(hoverState, 0x3C);
|
||||
try
|
||||
{
|
||||
this.HoveredActionChanged?.Invoke(this, this.HoveredAction);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(e, "Could not dispatch HoveredItemChanged event.");
|
||||
}
|
||||
this.HoveredActionChanged?.Raise(this, this.HoveredAction);
|
||||
|
||||
Log.Verbose("HoverActionId: {0}/{1} this:{2}", actionKind, actionId, hoverState.ToInt64().ToString("X"));
|
||||
}
|
||||
|
|
@ -562,14 +548,7 @@ namespace Dalamud.Game.Gui
|
|||
// TODO(goat): We should read this from memory directly, instead of relying on catching every toggle.
|
||||
this.GameUiHidden = !this.GameUiHidden;
|
||||
|
||||
try
|
||||
{
|
||||
this.UiHideToggled?.Invoke(this, this.GameUiHidden);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Error on OnUiHideToggled event dispatch");
|
||||
}
|
||||
this.UiHideToggled?.Raise(this, this.GameUiHidden);
|
||||
|
||||
Log.Debug("UiHide toggled: {0}", this.GameUiHidden);
|
||||
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ namespace Dalamud.Game.Network.Internal
|
|||
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
|
||||
}
|
||||
|
||||
this.CfPop?.Invoke(this, cfCondition);
|
||||
this.CfPop?.Raise(this, cfCondition);
|
||||
}).ContinueWith((task) => Log.Error(task.Exception, "CfPop.Invoke failed."), TaskContinuationOptions.OnlyOnFaulted);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -773,7 +773,7 @@ namespace Dalamud.Interface.Internal
|
|||
var customFontFirstConfigIndex = ioFonts.ConfigData.Size;
|
||||
|
||||
Log.Verbose("[FONT] Invoke OnBuildFonts");
|
||||
this.BuildFonts?.Invoke();
|
||||
this.BuildFonts?.Raise();
|
||||
Log.Verbose("[FONT] OnBuildFonts OK!");
|
||||
|
||||
for (int i = customFontFirstConfigIndex, i_ = ioFonts.ConfigData.Size; i < i_; i++)
|
||||
|
|
@ -881,7 +881,7 @@ namespace Dalamud.Interface.Internal
|
|||
}
|
||||
|
||||
Log.Verbose("[FONT] Invoke OnAfterBuildFonts");
|
||||
this.AfterBuildFonts?.Invoke();
|
||||
this.AfterBuildFonts?.Raise();
|
||||
Log.Verbose("[FONT] OnAfterBuildFonts OK!");
|
||||
|
||||
if (ioFonts.Fonts[0].NativePtr != DefaultFont.NativePtr)
|
||||
|
|
@ -978,7 +978,7 @@ namespace Dalamud.Interface.Internal
|
|||
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
||||
#endif
|
||||
|
||||
this.ResizeBuffers?.Invoke();
|
||||
this.ResizeBuffers?.Raise();
|
||||
|
||||
// We have to ensure we're working with the main swapchain,
|
||||
// as viewports might be resizing as well
|
||||
|
|
@ -1105,7 +1105,7 @@ namespace Dalamud.Interface.Internal
|
|||
var snap = ImGuiManagedAsserts.GetSnapshot();
|
||||
|
||||
if (this.IsDispatchingEvents)
|
||||
this.Draw?.Invoke();
|
||||
this.Draw?.Raise();
|
||||
|
||||
ImGuiManagedAsserts.ReportProblems("Dalamud Core", snap);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Dalamud.Interface.GameFonts;
|
|||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Utility;
|
||||
using ImGuiNET;
|
||||
using ImGuiScene;
|
||||
using Serilog;
|
||||
|
|
@ -392,7 +393,7 @@ namespace Dalamud.Interface
|
|||
/// </summary>
|
||||
internal void OpenConfig()
|
||||
{
|
||||
this.OpenConfigUi?.Invoke();
|
||||
this.OpenConfigUi?.Raise();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -400,7 +401,7 @@ namespace Dalamud.Interface
|
|||
/// </summary>
|
||||
internal void NotifyHideUi()
|
||||
{
|
||||
this.HideUi?.Invoke();
|
||||
this.HideUi?.Raise();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -408,7 +409,7 @@ namespace Dalamud.Interface
|
|||
/// </summary>
|
||||
internal void NotifyShowUi()
|
||||
{
|
||||
this.ShowUi?.Invoke();
|
||||
this.ShowUi?.Raise();
|
||||
}
|
||||
|
||||
private void OnDraw()
|
||||
|
|
@ -428,7 +429,7 @@ namespace Dalamud.Interface
|
|||
if (!this.lastFrameUiHideState)
|
||||
{
|
||||
this.lastFrameUiHideState = true;
|
||||
this.HideUi?.Invoke();
|
||||
this.HideUi?.Raise();
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -437,7 +438,7 @@ namespace Dalamud.Interface
|
|||
if (this.lastFrameUiHideState)
|
||||
{
|
||||
this.lastFrameUiHideState = false;
|
||||
this.ShowUi?.Invoke();
|
||||
this.ShowUi?.Raise();
|
||||
}
|
||||
|
||||
if (!this.interfaceManager.FontsReady)
|
||||
|
|
@ -470,7 +471,7 @@ namespace Dalamud.Interface
|
|||
|
||||
try
|
||||
{
|
||||
this.Draw?.Invoke();
|
||||
this.Draw?.Raise();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -503,17 +504,17 @@ namespace Dalamud.Interface
|
|||
|
||||
private void OnBuildFonts()
|
||||
{
|
||||
this.BuildFonts?.Invoke();
|
||||
this.BuildFonts?.Raise();
|
||||
}
|
||||
|
||||
private void OnAfterBuildFonts()
|
||||
{
|
||||
this.AfterBuildFonts?.Invoke();
|
||||
this.AfterBuildFonts?.Raise();
|
||||
}
|
||||
|
||||
private void OnResizeBuffers()
|
||||
{
|
||||
this.ResizeBuffers?.Invoke();
|
||||
this.ResizeBuffers?.Raise();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1262,28 +1262,14 @@ internal partial class PluginManager : IDisposable, IServiceType
|
|||
{
|
||||
this.DetectAvailablePluginUpdates();
|
||||
|
||||
try
|
||||
{
|
||||
this.OnAvailablePluginsChanged?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Error notifying {nameof(this.OnAvailablePluginsChanged)}");
|
||||
}
|
||||
this.OnAvailablePluginsChanged?.Raise();
|
||||
}
|
||||
|
||||
private void NotifyInstalledPluginsChanged()
|
||||
{
|
||||
this.DetectAvailablePluginUpdates();
|
||||
|
||||
try
|
||||
{
|
||||
this.OnInstalledPluginsChanged?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, $"Error notifying {nameof(this.OnInstalledPluginsChanged)}");
|
||||
}
|
||||
this.OnInstalledPluginsChanged?.Raise();
|
||||
}
|
||||
|
||||
private static class Locs
|
||||
|
|
|
|||
98
Dalamud/Utility/EventHandlerExtensions.cs
Normal file
98
Dalamud/Utility/EventHandlerExtensions.cs
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Dalamud.Game;
|
||||
using Serilog;
|
||||
|
||||
using static Dalamud.Game.Framework;
|
||||
|
||||
namespace Dalamud.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for Events.
|
||||
/// </summary>
|
||||
internal static class EventHandlerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Replacement for Invoke() on EventHandlers to catch exceptions that stop event propagation in case
|
||||
/// of a thrown Exception inside of an invocation.
|
||||
/// </summary>
|
||||
/// <param name="eh">The EventHandler in question.</param>
|
||||
/// <param name="sender">Default sender for Invoke equivalent.</param>
|
||||
/// <param name="e">Default EventArgs for Invoke equivalent.</param>
|
||||
public static void Raise(this EventHandler eh, object sender, EventArgs e)
|
||||
{
|
||||
if (eh == null)
|
||||
return;
|
||||
|
||||
foreach (var handler in eh.GetInvocationList().Cast<EventHandler>())
|
||||
{
|
||||
HandleRaise(() => handler(sender, e));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for Invoke() on generic EventHandlers to catch exceptions that stop event propagation in case
|
||||
/// of a thrown Exception inside of an invocation.
|
||||
/// </summary>
|
||||
/// <param name="eh">The EventHandler in question.</param>
|
||||
/// <param name="sender">Default sender for Invoke equivalent.</param>
|
||||
/// <param name="e">Default EventArgs for Invoke equivalent.</param>
|
||||
/// <typeparam name="T">Type of EventArgs.</typeparam>
|
||||
public static void Raise<T>(this EventHandler<T> eh, object sender, T e)
|
||||
{
|
||||
if (eh == null)
|
||||
return;
|
||||
|
||||
foreach (var handler in eh.GetInvocationList().Cast<EventHandler<T>>())
|
||||
{
|
||||
HandleRaise(() => handler(sender, e));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for Invoke() on event Actions to catch exceptions that stop event propagation in case
|
||||
/// of a thrown Exception inside of an invocation.
|
||||
/// </summary>
|
||||
/// <param name="act">The Action in question.</param>
|
||||
public static void Raise(this Action act)
|
||||
{
|
||||
if (act == null)
|
||||
return;
|
||||
|
||||
foreach (var action in act.GetInvocationList().Cast<Action>())
|
||||
{
|
||||
HandleRaise(action);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replacement for Invoke() on OnUpdateDelegate to catch exceptions that stop event propagation in case
|
||||
/// of a thrown Exception inside of an invocation.
|
||||
/// </summary>
|
||||
/// <param name="updateDelegate">The OnUpdateDelegate in question.</param>
|
||||
/// <param name="framework">Framework to be passed on to OnUpdateDelegate.</param>
|
||||
public static void Raise(this OnUpdateDelegate updateDelegate, Framework framework)
|
||||
{
|
||||
if (updateDelegate == null)
|
||||
return;
|
||||
|
||||
foreach (var action in updateDelegate.GetInvocationList().Cast<OnUpdateDelegate>())
|
||||
{
|
||||
HandleRaise(() => action(framework));
|
||||
}
|
||||
}
|
||||
|
||||
private static void HandleRaise(Action act)
|
||||
{
|
||||
try
|
||||
{
|
||||
act();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception during raise of {handler}", act.Method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue