From b395e92faae0a197a2d1c2e10e835e38fcc6502c Mon Sep 17 00:00:00 2001 From: Chase W Date: Mon, 15 Jun 2020 15:28:03 -0400 Subject: [PATCH] Implemented event priority attribute --- src/SMAPI/Events/EventPriority.cs | 29 ++++++++++++++ src/SMAPI/Events/EventPriorityAttribute.cs | 29 ++++++++++++++ src/SMAPI/Framework/Events/ManagedEvent.cs | 45 +++++++++++++++++----- 3 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/SMAPI/Events/EventPriority.cs create mode 100644 src/SMAPI/Events/EventPriorityAttribute.cs diff --git a/src/SMAPI/Events/EventPriority.cs b/src/SMAPI/Events/EventPriority.cs new file mode 100644 index 00000000..17f5fbb7 --- /dev/null +++ b/src/SMAPI/Events/EventPriority.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StardewModdingAPI.Events +{ + /// + /// Event priority for method handlers. + /// + public enum EventPriority + { + /// + /// Low priority. + /// + Low = 3, + + /// + /// Normal priority. This is the default. + /// + Normal = 2, + + /// + /// High priority. + /// + High = 1, + } +} diff --git a/src/SMAPI/Events/EventPriorityAttribute.cs b/src/SMAPI/Events/EventPriorityAttribute.cs new file mode 100644 index 00000000..c5683931 --- /dev/null +++ b/src/SMAPI/Events/EventPriorityAttribute.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace StardewModdingAPI.Events +{ + /// + /// An attribute for controlling event priority of an event handler. + /// + [AttributeUsage(AttributeTargets.Method)] + public class EventPriorityAttribute : System.Attribute + { + /// + /// The priority for the method marked by this attribute. + /// + public EventPriority Priority { get; } + + /// + /// Constructor. + /// + /// The priority for method marked by this attribute. + public EventPriorityAttribute( EventPriority priority ) + { + this.Priority = priority; + } + } +} diff --git a/src/SMAPI/Framework/Events/ManagedEvent.cs b/src/SMAPI/Framework/Events/ManagedEvent.cs index 118b73ac..172b25c0 100644 --- a/src/SMAPI/Framework/Events/ManagedEvent.cs +++ b/src/SMAPI/Framework/Events/ManagedEvent.cs @@ -1,6 +1,9 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; +using StardewModdingAPI.Events; using StardewModdingAPI.Framework.PerformanceMonitoring; namespace StardewModdingAPI.Framework.Events @@ -13,7 +16,7 @@ namespace StardewModdingAPI.Framework.Events ** Fields *********/ /// The underlying event. - private event EventHandler Event; + private IList> EventHandlers = new List>(); /// Writes messages to the log. private readonly IMonitor Monitor; @@ -77,23 +80,23 @@ namespace StardewModdingAPI.Framework.Events /// The mod which added the event handler. public void Add(EventHandler handler, IModMetadata mod) { - this.Event += handler; - this.AddTracking(mod, handler, this.Event?.GetInvocationList().Cast>()); + this.EventHandlers.Add(handler); + this.AddTracking(mod, handler, this.EventHandlers); } /// Remove an event handler. /// The event handler. public void Remove(EventHandler handler) { - this.Event -= handler; - this.RemoveTracking(handler, this.Event?.GetInvocationList().Cast>()); + this.EventHandlers.Remove(handler); + this.RemoveTracking(handler, this.EventHandlers); } /// Raise the event and notify all handlers. /// The event arguments to pass. public void Raise(TEventArgs args) { - if (this.Event == null) + if (this.EventHandlers.Count == 0) return; @@ -118,7 +121,7 @@ namespace StardewModdingAPI.Framework.Events /// A lambda which returns true if the event should be raised for the given mod. public void RaiseForMods(TEventArgs args, Func match) { - if (this.Event == null) + if (this.EventHandlers.Count == 0) return; foreach (EventHandler handler in this.CachedInvocationList) @@ -154,6 +157,30 @@ namespace StardewModdingAPI.Framework.Events : mod.DisplayName; } + /// + /// Get the event priority of an event handler. + /// + /// The event handler to get the priority of. + /// The event priority of the event handler. + private EventPriority GetPriorityOfHandler(EventHandler handler) + { + CustomAttributeData attr = handler.Method.CustomAttributes.FirstOrDefault(a => a.AttributeType == typeof(EventPriorityAttribute)); + if (attr == null) + return EventPriority.Normal; + return (EventPriority) attr.ConstructorArguments[0].Value; + } + + /// + /// Sort an invocation list by its priority. + /// + /// The invocation list. + /// An array of the event handlers sorted by their priority. + private EventHandler[] GetCachedInvocationList(IEnumerable> invocationList ) + { + EventHandler[] handlers = invocationList?.ToArray() ?? new EventHandler[0]; + return handlers.OrderBy((h1) => this.GetPriorityOfHandler(h1)).ToArray(); + } + /// Track an event handler. /// The mod which added the handler. /// The event handler. @@ -161,7 +188,7 @@ namespace StardewModdingAPI.Framework.Events protected void AddTracking(IModMetadata mod, EventHandler handler, IEnumerable> invocationList) { this.SourceMods[handler] = mod; - this.CachedInvocationList = invocationList?.ToArray() ?? new EventHandler[0]; + this.CachedInvocationList = this.GetCachedInvocationList(invocationList); } /// Remove tracking for an event handler. @@ -169,7 +196,7 @@ namespace StardewModdingAPI.Framework.Events /// The updated event invocation list. protected void RemoveTracking(EventHandler handler, IEnumerable> invocationList) { - this.CachedInvocationList = invocationList?.ToArray() ?? new EventHandler[0]; + this.CachedInvocationList = this.GetCachedInvocationList(invocationList); if (!this.CachedInvocationList.Contains(handler)) // don't remove if there's still a reference to the removed handler (e.g. it was added twice and removed once) this.SourceMods.Remove(handler); }