summaryrefslogtreecommitdiff
path: root/src/SMAPI/Framework/Events
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Framework/Events')
-rw-r--r--src/SMAPI/Framework/Events/EventManager.cs48
-rw-r--r--src/SMAPI/Framework/Events/IManagedEvent.cs15
-rw-r--r--src/SMAPI/Framework/Events/ManagedEvent.cs58
3 files changed, 91 insertions, 30 deletions
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index 892cbc7b..a9dfda97 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -1,5 +1,8 @@
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
using StardewModdingAPI.Events;
+using StardewModdingAPI.Framework.PerformanceMonitoring;
namespace StardewModdingAPI.Framework.Events
{
@@ -173,28 +176,32 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>Construct an instance.</summary>
/// <param name="monitor">Writes messages to the log.</param>
/// <param name="modRegistry">The mod registry with which to identify mods.</param>
- public EventManager(IMonitor monitor, ModRegistry modRegistry)
+ /// <param name="performanceMonitor">Tracks performance metrics.</param>
+ public EventManager(IMonitor monitor, ModRegistry modRegistry, PerformanceMonitor performanceMonitor)
{
// create shortcut initializers
- ManagedEvent<TEventArgs> ManageEventOf<TEventArgs>(string typeName, string eventName) => new ManagedEvent<TEventArgs>($"{typeName}.{eventName}", monitor, modRegistry);
+ ManagedEvent<TEventArgs> ManageEventOf<TEventArgs>(string typeName, string eventName, bool isPerformanceCritical = false)
+ {
+ return new ManagedEvent<TEventArgs>($"{typeName}.{eventName}", monitor, modRegistry, performanceMonitor, isPerformanceCritical);
+ }
// init events (new)
this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged));
- this.Rendering = ManageEventOf<RenderingEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering));
- this.Rendered = ManageEventOf<RenderedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendered));
- this.RenderingWorld = ManageEventOf<RenderingWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingWorld));
- this.RenderedWorld = ManageEventOf<RenderedWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedWorld));
- this.RenderingActiveMenu = ManageEventOf<RenderingActiveMenuEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingActiveMenu));
- this.RenderedActiveMenu = ManageEventOf<RenderedActiveMenuEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedActiveMenu));
- this.RenderingHud = ManageEventOf<RenderingHudEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingHud));
- this.RenderedHud = ManageEventOf<RenderedHudEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedHud));
+ this.Rendering = ManageEventOf<RenderingEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true);
+ this.Rendered = ManageEventOf<RenderedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendered), isPerformanceCritical: true);
+ this.RenderingWorld = ManageEventOf<RenderingWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingWorld), isPerformanceCritical: true);
+ this.RenderedWorld = ManageEventOf<RenderedWorldEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedWorld), isPerformanceCritical: true);
+ this.RenderingActiveMenu = ManageEventOf<RenderingActiveMenuEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingActiveMenu), isPerformanceCritical: true);
+ this.RenderedActiveMenu = ManageEventOf<RenderedActiveMenuEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedActiveMenu), isPerformanceCritical: true);
+ this.RenderingHud = ManageEventOf<RenderingHudEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderingHud), isPerformanceCritical: true);
+ this.RenderedHud = ManageEventOf<RenderedHudEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.RenderedHud), isPerformanceCritical: true);
this.WindowResized = ManageEventOf<WindowResizedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.WindowResized));
this.GameLaunched = ManageEventOf<GameLaunchedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.GameLaunched));
- this.UpdateTicking = ManageEventOf<UpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicking));
- this.UpdateTicked = ManageEventOf<UpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicked));
- this.OneSecondUpdateTicking = ManageEventOf<OneSecondUpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicking));
- this.OneSecondUpdateTicked = ManageEventOf<OneSecondUpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicked));
+ this.UpdateTicking = ManageEventOf<UpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicking), isPerformanceCritical: true);
+ this.UpdateTicked = ManageEventOf<UpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.UpdateTicked), isPerformanceCritical: true);
+ this.OneSecondUpdateTicking = ManageEventOf<OneSecondUpdateTickingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicking), isPerformanceCritical: true);
+ this.OneSecondUpdateTicked = ManageEventOf<OneSecondUpdateTickedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.OneSecondUpdateTicked), isPerformanceCritical: true);
this.SaveCreating = ManageEventOf<SaveCreatingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreating));
this.SaveCreated = ManageEventOf<SaveCreatedEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.SaveCreated));
this.Saving = ManageEventOf<SavingEventArgs>(nameof(IModEvents.GameLoop), nameof(IGameLoopEvents.Saving));
@@ -207,7 +214,7 @@ namespace StardewModdingAPI.Framework.Events
this.ButtonPressed = ManageEventOf<ButtonPressedEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.ButtonPressed));
this.ButtonReleased = ManageEventOf<ButtonReleasedEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.ButtonReleased));
- this.CursorMoved = ManageEventOf<CursorMovedEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.CursorMoved));
+ this.CursorMoved = ManageEventOf<CursorMovedEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.CursorMoved), isPerformanceCritical: true);
this.MouseWheelScrolled = ManageEventOf<MouseWheelScrolledEventArgs>(nameof(IModEvents.Input), nameof(IInputEvents.MouseWheelScrolled));
this.PeerContextReceived = ManageEventOf<PeerContextReceivedEventArgs>(nameof(IModEvents.Multiplayer), nameof(IMultiplayerEvents.PeerContextReceived));
@@ -228,8 +235,15 @@ namespace StardewModdingAPI.Framework.Events
this.TerrainFeatureListChanged = ManageEventOf<TerrainFeatureListChangedEventArgs>(nameof(IModEvents.World), nameof(IWorldEvents.TerrainFeatureListChanged));
this.LoadStageChanged = ManageEventOf<LoadStageChangedEventArgs>(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.LoadStageChanged));
- this.UnvalidatedUpdateTicking = ManageEventOf<UnvalidatedUpdateTickingEventArgs>(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicking));
- this.UnvalidatedUpdateTicked = ManageEventOf<UnvalidatedUpdateTickedEventArgs>(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicked));
+ this.UnvalidatedUpdateTicking = ManageEventOf<UnvalidatedUpdateTickingEventArgs>(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicking), isPerformanceCritical: true);
+ this.UnvalidatedUpdateTicked = ManageEventOf<UnvalidatedUpdateTickedEventArgs>(nameof(IModEvents.Specialized), nameof(ISpecializedEvents.UnvalidatedUpdateTicked), isPerformanceCritical: true);
+ }
+
+ /// <summary>Get all managed events.</summary>
+ public IEnumerable<IManagedEvent> GetAllEvents()
+ {
+ foreach (FieldInfo field in this.GetType().GetFields())
+ yield return (IManagedEvent)field.GetValue(this);
}
}
}
diff --git a/src/SMAPI/Framework/Events/IManagedEvent.cs b/src/SMAPI/Framework/Events/IManagedEvent.cs
new file mode 100644
index 00000000..e4e3ca08
--- /dev/null
+++ b/src/SMAPI/Framework/Events/IManagedEvent.cs
@@ -0,0 +1,15 @@
+namespace StardewModdingAPI.Framework.Events
+{
+ /// <summary>Metadata for an event raised by SMAPI.</summary>
+ internal interface IManagedEvent
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A human-readable name for the event.</summary>
+ string EventName { get; }
+
+ /// <summary>Whether the event is typically called at least once per second.</summary>
+ bool IsPerformanceCritical { get; }
+ }
+}
diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs
index 2afe7a03..118b73ac 100644
--- a/src/SMAPI/Framework/Events/ManagedEvent.cs
+++ b/src/SMAPI/Framework/Events/ManagedEvent.cs
@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using StardewModdingAPI.Framework.PerformanceMonitoring;
namespace StardewModdingAPI.Framework.Events
{
/// <summary>An event wrapper which intercepts and logs errors in handler code.</summary>
/// <typeparam name="TEventArgs">The event arguments type.</typeparam>
- internal class ManagedEvent<TEventArgs>
+ internal class ManagedEvent<TEventArgs> : IManagedEvent
{
/*********
** Fields
@@ -14,9 +15,6 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>The underlying event.</summary>
private event EventHandler<TEventArgs> Event;
- /// <summary>A human-readable name for the event.</summary>
- private readonly string EventName;
-
/// <summary>Writes messages to the log.</summary>
private readonly IMonitor Monitor;
@@ -29,6 +27,19 @@ namespace StardewModdingAPI.Framework.Events
/// <summary>The cached invocation list.</summary>
private EventHandler<TEventArgs>[] CachedInvocationList;
+ /// <summary>Tracks performance metrics.</summary>
+ private readonly PerformanceMonitor PerformanceMonitor;
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>A human-readable name for the event.</summary>
+ public string EventName { get; }
+
+ /// <summary>Whether the event is typically called at least once per second.</summary>
+ public bool IsPerformanceCritical { get; }
+
/*********
** Public methods
@@ -37,11 +48,15 @@ namespace StardewModdingAPI.Framework.Events
/// <param name="eventName">A human-readable name for the event.</param>
/// <param name="monitor">Writes messages to the log.</param>
/// <param name="modRegistry">The mod registry with which to identify mods.</param>
- public ManagedEvent(string eventName, IMonitor monitor, ModRegistry modRegistry)
+ /// <param name="performanceMonitor">Tracks performance metrics.</param>
+ /// <param name="isPerformanceCritical">Whether the event is typically called at least once per second.</param>
+ public ManagedEvent(string eventName, IMonitor monitor, ModRegistry modRegistry, PerformanceMonitor performanceMonitor, bool isPerformanceCritical = false)
{
this.EventName = eventName;
this.Monitor = monitor;
this.ModRegistry = modRegistry;
+ this.PerformanceMonitor = performanceMonitor;
+ this.IsPerformanceCritical = isPerformanceCritical;
}
/// <summary>Get whether anything is listening to the event.</summary>
@@ -81,17 +96,21 @@ namespace StardewModdingAPI.Framework.Events
if (this.Event == null)
return;
- foreach (EventHandler<TEventArgs> handler in this.CachedInvocationList)
+
+ this.PerformanceMonitor.Track(this.EventName, () =>
{
- try
+ foreach (EventHandler<TEventArgs> handler in this.CachedInvocationList)
{
- handler.Invoke(null, args);
- }
- catch (Exception ex)
- {
- this.LogError(handler, ex);
+ try
+ {
+ this.PerformanceMonitor.Track(this.EventName, this.GetModNameForPerformanceCounters(handler), () => handler.Invoke(null, args));
+ }
+ catch (Exception ex)
+ {
+ this.LogError(handler, ex);
+ }
}
- }
+ });
}
/// <summary>Raise the event and notify all handlers.</summary>
@@ -122,6 +141,19 @@ namespace StardewModdingAPI.Framework.Events
/*********
** Private methods
*********/
+ /// <summary>Get the mod name for a given event handler to display in performance monitoring reports.</summary>
+ /// <param name="handler">The event handler.</param>
+ private string GetModNameForPerformanceCounters(EventHandler<TEventArgs> handler)
+ {
+ IModMetadata mod = this.GetSourceMod(handler);
+ if (mod == null)
+ return Constants.GamePerformanceCounterName;
+
+ return mod.HasManifest()
+ ? mod.Manifest.UniqueID
+ : mod.DisplayName;
+ }
+
/// <summary>Track an event handler.</summary>
/// <param name="mod">The mod which added the handler.</param>
/// <param name="handler">The event handler.</param>