Merge pull request #994 from rootdarkarchon/master

This commit is contained in:
goat 2022-09-03 23:46:44 +02:00 committed by GitHub
commit 546039fa11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 165 additions and 99 deletions

View file

@ -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?.InvokeSafely(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?.InvokeSafely(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?.InvokeSafely(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?.InvokeSafely(this, null);
gameGui.ResetUiHideState();
}
@ -193,11 +194,11 @@ namespace Dalamud.Game.ClientState
if (this.IsPvP)
{
this.EnterPvP?.Invoke();
this.EnterPvP?.InvokeSafely();
}
else
{
this.LeavePvP?.Invoke();
this.LeavePvP?.InvokeSafely();
}
}
}

View file

@ -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?.InvokeSafely(this);
}
}
original:
original:
return this.updateHook.OriginalDisposeSafe(framework);
}

View file

@ -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?.InvokeSafely(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?.InvokeSafely(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?.InvokeSafely(this, this.GameUiHidden);
Log.Debug("UiHide toggled: {0}", this.GameUiHidden);

View file

@ -288,7 +288,7 @@ namespace Dalamud.Game.Network.Internal
Service<ChatGui>.GetNullable()?.Print($"Duty pop: {cfcName}");
}
this.CfPop?.Invoke(this, cfCondition);
this.CfPop?.InvokeSafely(this, cfCondition);
}).ContinueWith((task) => Log.Error(task.Exception, "CfPop.Invoke failed."), TaskContinuationOptions.OnlyOnFaulted);
}
}

View file

@ -773,7 +773,7 @@ namespace Dalamud.Interface.Internal
var customFontFirstConfigIndex = ioFonts.ConfigData.Size;
Log.Verbose("[FONT] Invoke OnBuildFonts");
this.BuildFonts?.Invoke();
this.BuildFonts?.InvokeSafely();
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?.InvokeSafely();
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?.InvokeSafely();
// We have to ensure we're working with the main swapchain,
// as viewports might be resizing as well

View file

@ -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?.InvokeSafely();
}
/// <summary>
@ -400,7 +401,7 @@ namespace Dalamud.Interface
/// </summary>
internal void NotifyHideUi()
{
this.HideUi?.Invoke();
this.HideUi?.InvokeSafely();
}
/// <summary>
@ -408,7 +409,7 @@ namespace Dalamud.Interface
/// </summary>
internal void NotifyShowUi()
{
this.ShowUi?.Invoke();
this.ShowUi?.InvokeSafely();
}
private void OnDraw()
@ -428,7 +429,7 @@ namespace Dalamud.Interface
if (!this.lastFrameUiHideState)
{
this.lastFrameUiHideState = true;
this.HideUi?.Invoke();
this.HideUi?.InvokeSafely();
}
return;
@ -437,7 +438,7 @@ namespace Dalamud.Interface
if (this.lastFrameUiHideState)
{
this.lastFrameUiHideState = false;
this.ShowUi?.Invoke();
this.ShowUi?.InvokeSafely();
}
if (!this.interfaceManager.FontsReady)
@ -470,7 +471,7 @@ namespace Dalamud.Interface
try
{
this.Draw?.Invoke();
this.Draw?.InvokeSafely();
}
catch (Exception ex)
{
@ -503,17 +504,17 @@ namespace Dalamud.Interface
private void OnBuildFonts()
{
this.BuildFonts?.Invoke();
this.BuildFonts?.InvokeSafely();
}
private void OnAfterBuildFonts()
{
this.AfterBuildFonts?.Invoke();
this.AfterBuildFonts?.InvokeSafely();
}
private void OnResizeBuffers()
{
this.ResizeBuffers?.Invoke();
this.ResizeBuffers?.InvokeSafely();
}
}
}

View file

@ -1263,28 +1263,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?.InvokeSafely();
}
private void NotifyInstalledPluginsChanged()
{
this.DetectAvailablePluginUpdates();
try
{
this.OnInstalledPluginsChanged?.Invoke();
}
catch (Exception ex)
{
Log.Error(ex, $"Error notifying {nameof(this.OnInstalledPluginsChanged)}");
}
this.OnInstalledPluginsChanged?.InvokeSafely();
}
private static class Locs

View 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 InvokeSafely(this EventHandler eh, object sender, EventArgs e)
{
if (eh == null)
return;
foreach (var handler in eh.GetInvocationList().Cast<EventHandler>())
{
HandleInvoke(() => 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 InvokeSafely<T>(this EventHandler<T> eh, object sender, T e)
{
if (eh == null)
return;
foreach (var handler in eh.GetInvocationList().Cast<EventHandler<T>>())
{
HandleInvoke(() => 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 InvokeSafely(this Action act)
{
if (act == null)
return;
foreach (var action in act.GetInvocationList().Cast<Action>())
{
HandleInvoke(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 InvokeSafely(this OnUpdateDelegate updateDelegate, Framework framework)
{
if (updateDelegate == null)
return;
foreach (var action in updateDelegate.GetInvocationList().Cast<OnUpdateDelegate>())
{
HandleInvoke(() => action(framework));
}
}
private static void HandleInvoke(Action act)
{
try
{
act();
}
catch (Exception ex)
{
Log.Error(ex, "Exception during raise of {handler}", act.Method);
}
}
}
}