From ff75c787e4ae8d4d5e97e146fa67b0a3e11cda83 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sat, 1 Feb 2025 03:02:05 +0100 Subject: [PATCH 1/3] Add priority for event handlers to Exiled Events --- EXILED/Exiled.Events/Features/Event.cs | 130 ++++++++++++++++++---- EXILED/Exiled.Events/Features/Event{T}.cs | 127 +++++++++++++++++---- 2 files changed, 213 insertions(+), 44 deletions(-) diff --git a/EXILED/Exiled.Events/Features/Event.cs b/EXILED/Exiled.Events/Features/Event.cs index 9885747c7..b06f3117e 100644 --- a/EXILED/Exiled.Events/Features/Event.cs +++ b/EXILED/Exiled.Events/Features/Event.cs @@ -14,6 +14,7 @@ namespace Exiled.Events.Features using Exiled.API.Features; using Exiled.Events.EventArgs.Interfaces; using MEC; + using PluginAPI.Roles; /// /// The custom delegate, with empty parameters. @@ -31,8 +32,20 @@ namespace Exiled.Events.Features /// public class Event : IExiledEvent { + private record Registration(CustomEventHandler handler, int priority); + + private record AsyncRegistration(CustomAsyncEventHandler handler, int priority); + private static readonly List EventsValue = new(); + private static readonly IComparer RegisterComparable = Comparer.Create((x, y) => y.priority - x.priority); + + private static readonly IComparer AsyncRegisterComparable = Comparer.Create((x, y) => y.priority - x.priority); + + private readonly List innerEvent = new(); + + private readonly List innerAsyncEvent = new(); + private bool patched; /// @@ -43,10 +56,6 @@ public Event() EventsValue.Add(this); } - private event CustomEventHandler InnerEvent; - - private event CustomAsyncEventHandler InnerAsyncEvent; - /// /// Gets a of which contains all the instances. /// @@ -105,6 +114,16 @@ public Event() /// /// The handler to add. public void Subscribe(CustomEventHandler handler) + => Subscribe(handler, 0); + + // I try to do inheritated summary but the analizer refuse + + /// + /// Subscribes a target to the inner event if the conditional is true. + /// + /// The handler to add. + /// The highest priority is the first called, the lowest the last. + public void Subscribe(CustomEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); @@ -114,7 +133,18 @@ public void Subscribe(CustomEventHandler handler) patched = true; } - InnerEvent += handler; + Registration registration = new Registration(handler, priority); + int index = innerEvent.BinarySearch(registration, RegisterComparable); + if (index < 0) + { + innerEvent.Insert(~index, registration); + } + else + { + while (index < innerEvent.Count && innerEvent[index].priority == priority) + index++; + innerEvent.Insert(index, registration); + } } /// @@ -122,6 +152,14 @@ public void Subscribe(CustomEventHandler handler) /// /// The handler to add. public void Subscribe(CustomAsyncEventHandler handler) + => Subscribe(handler, 0); + + /// + /// Subscribes a target to the inner event if the conditional is true. + /// + /// The handler to add. + /// The highest priority is the first called, the lowest the last. + public void Subscribe(CustomAsyncEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); @@ -131,7 +169,18 @@ public void Subscribe(CustomAsyncEventHandler handler) patched = true; } - InnerAsyncEvent += handler; + AsyncRegistration registration = new AsyncRegistration(handler, 0); + int index = innerAsyncEvent.BinarySearch(registration, AsyncRegisterComparable); + if (index < 0) + { + innerAsyncEvent.Insert(~index, registration); + } + else + { + while (index < innerAsyncEvent.Count && innerAsyncEvent[index].priority == priority) + index++; + innerAsyncEvent.Insert(index, registration); + } } /// @@ -140,7 +189,9 @@ public void Subscribe(CustomAsyncEventHandler handler) /// The handler to add. public void Unsubscribe(CustomEventHandler handler) { - InnerEvent -= handler; + int index = innerEvent.FindIndex(p => p.handler == handler); + if (index != -1) + innerEvent.RemoveAt(index); } /// @@ -149,7 +200,9 @@ public void Unsubscribe(CustomEventHandler handler) /// The handler to add. public void Unsubscribe(CustomAsyncEventHandler handler) { - InnerAsyncEvent -= handler; + int index = innerAsyncEvent.FindIndex(p => p.handler == handler); + if (index != -1) + innerAsyncEvent.RemoveAt(index); } /// @@ -157,25 +210,61 @@ public void Unsubscribe(CustomAsyncEventHandler handler) /// public void InvokeSafely() { - InvokeNormal(); - InvokeAsync(); + BlendedInvoke(); + + // InvokeNormal(); + // InvokeAsync(); } /// - internal void InvokeNormal() + internal void BlendedInvoke() { - if (InnerEvent is null) - return; + int count = innerEvent.Count + innerAsyncEvent.Count; + int eventIndex = 0, asyncEventIndex = 0; - foreach (CustomEventHandler handler in InnerEvent.GetInvocationList().Cast()) + for (int i = 0; i < count; i++) + { + if (eventIndex < innerEvent.Count && (asyncEventIndex >= innerAsyncEvent.Count || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) + { + try + { + innerEvent[eventIndex].handler(); + } + catch (Exception ex) + { + Log.Error($"Method \"{innerEvent[eventIndex].handler.Method.Name}\" of the class \"{innerEvent[eventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + } + + eventIndex++; + } + else + { + try + { + Timing.RunCoroutine(innerAsyncEvent[asyncEventIndex].handler()); + } + catch (Exception ex) + { + Log.Error($"Method \"{innerAsyncEvent[asyncEventIndex].handler.Method.Name}\" of the class \"{innerAsyncEvent[asyncEventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + } + + asyncEventIndex++; + } + } + } + + /// + internal void InvokeNormal() + { + foreach (Registration registration in innerEvent) { try { - handler(); + registration.handler(); } catch (Exception ex) { - Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); } } } @@ -183,18 +272,15 @@ internal void InvokeNormal() /// internal void InvokeAsync() { - if (InnerAsyncEvent is null) - return; - - foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast()) + foreach (AsyncRegistration registration in innerAsyncEvent) { try { - Timing.RunCoroutine(handler()); + Timing.RunCoroutine(registration.handler()); } catch (Exception ex) { - Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); } } } diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs index e9b6e546b..7150316b5 100644 --- a/EXILED/Exiled.Events/Features/Event{T}.cs +++ b/EXILED/Exiled.Events/Features/Event{T}.cs @@ -36,8 +36,20 @@ namespace Exiled.Events.Features /// The specified that the event will use. public class Event : IExiledEvent { + private record Registration(CustomEventHandler handler, int priority); + + private record AsyncRegistration(CustomAsyncEventHandler handler, int priority); + private static readonly Dictionary> TypeToEvent = new(); + private static readonly IComparer RegisterComparable = Comparer.Create((x, y) => y.priority - x.priority); + + private static readonly IComparer AsyncRegisterComparable = Comparer.Create((x, y) => y.priority - x.priority); + + private readonly List innerEvent = new(); + + private readonly List innerAsyncEvent = new(); + private bool patched; /// @@ -48,10 +60,6 @@ public Event() TypeToEvent.Add(typeof(T), this); } - private event CustomEventHandler InnerEvent; - - private event CustomAsyncEventHandler InnerAsyncEvent; - /// /// Gets a of which contains all the instances. /// @@ -110,6 +118,14 @@ public Event() /// /// The handler to add. public void Subscribe(CustomEventHandler handler) + => Subscribe(handler, 0); + + /// + /// Subscribes a target to the inner event if the conditional is true. + /// + /// The handler to add. + /// The highest priority is the first called, the lowest the last. + public void Subscribe(CustomEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); @@ -119,7 +135,18 @@ public void Subscribe(CustomEventHandler handler) patched = true; } - InnerEvent += handler; + Registration registration = new Registration(handler, priority); + int index = innerEvent.BinarySearch(registration, RegisterComparable); + if (index < 0) + { + innerEvent.Insert(~index, registration); + } + else + { + while (index < innerEvent.Count && innerEvent[index].priority == priority) + index++; + innerEvent.Insert(index, registration); + } } /// @@ -127,6 +154,14 @@ public void Subscribe(CustomEventHandler handler) /// /// The handler to add. public void Subscribe(CustomAsyncEventHandler handler) + => Subscribe(handler, 0); + + /// + /// Subscribes a target to the inner event if the conditional is true. + /// + /// The handler to add. + /// The highest priority is the first called, the lowest the last. + public void Subscribe(CustomAsyncEventHandler handler, int priority) { Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!"); @@ -136,7 +171,18 @@ public void Subscribe(CustomAsyncEventHandler handler) patched = true; } - InnerAsyncEvent += handler; + AsyncRegistration registration = new AsyncRegistration(handler, 0); + int index = innerAsyncEvent.BinarySearch(registration, AsyncRegisterComparable); + if (index < 0) + { + innerAsyncEvent.Insert(~index, registration); + } + else + { + while (index < innerAsyncEvent.Count && innerAsyncEvent[index].priority == priority) + index++; + innerAsyncEvent.Insert(index, registration); + } } /// @@ -145,7 +191,9 @@ public void Subscribe(CustomAsyncEventHandler handler) /// The handler to add. public void Unsubscribe(CustomEventHandler handler) { - InnerEvent -= handler; + int index = innerEvent.FindIndex(p => p.handler == handler); + if (index != -1) + innerEvent.RemoveAt(index); } /// @@ -154,7 +202,9 @@ public void Unsubscribe(CustomEventHandler handler) /// The handler to add. public void Unsubscribe(CustomAsyncEventHandler handler) { - InnerAsyncEvent -= handler; + int index = innerAsyncEvent.FindIndex(p => p.handler == handler); + if (index != -1) + innerAsyncEvent.RemoveAt(index); } /// @@ -164,25 +214,61 @@ public void Unsubscribe(CustomAsyncEventHandler handler) /// Event or its arg is . public void InvokeSafely(T arg) { - InvokeNormal(arg); - InvokeAsync(arg); + BlendedInvoke(arg); + + // InvokeNormal(arg); + // InvokeAsync(arg); } /// - internal void InvokeNormal(T arg) + internal void BlendedInvoke(T arg) { - if (InnerEvent is null) - return; + int count = innerEvent.Count + innerAsyncEvent.Count; + int eventIndex = 0, asyncEventIndex = 0; - foreach (CustomEventHandler handler in InnerEvent.GetInvocationList().Cast>()) + for (int i = 0; i < count; i++) + { + if (eventIndex < innerEvent.Count && (asyncEventIndex >= innerAsyncEvent.Count || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) + { + try + { + innerEvent[eventIndex].handler(arg); + } + catch (Exception ex) + { + Log.Error($"Method \"{innerEvent[eventIndex].handler.Method.Name}\" of the class \"{innerEvent[eventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + } + + eventIndex++; + } + else + { + try + { + Timing.RunCoroutine(innerAsyncEvent[asyncEventIndex].handler(arg)); + } + catch (Exception ex) + { + Log.Error($"Method \"{innerAsyncEvent[asyncEventIndex].handler.Method.Name}\" of the class \"{innerAsyncEvent[asyncEventIndex].handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + } + + asyncEventIndex++; + } + } + } + + /// + internal void InvokeNormal(T arg) + { + foreach (Registration registration in innerEvent) { try { - handler(arg); + registration.handler(arg); } catch (Exception ex) { - Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); } } } @@ -190,18 +276,15 @@ internal void InvokeNormal(T arg) /// internal void InvokeAsync(T arg) { - if (InnerAsyncEvent is null) - return; - - foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast>()) + foreach (AsyncRegistration registration in innerAsyncEvent) { try { - Timing.RunCoroutine(handler(arg)); + Timing.RunCoroutine(registration.handler(arg)); } catch (Exception ex) { - Log.Error($"Method \"{handler.Method.Name}\" of the class \"{handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); + Log.Error($"Method \"{registration.handler.Method.Name}\" of the class \"{registration.handler.Method.ReflectedType.FullName}\" caused an exception when handling the event \"{GetType().FullName}\"\n{ex}"); } } } From 9805a3489fcf76af88fe7e8dc623cf6c02c6d1c4 Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Sun, 2 Feb 2025 00:29:04 +0100 Subject: [PATCH 2/3] Protect inner event list from edition durring Invoke --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 958e3428c..722b747e7 100644 --- a/.gitignore +++ b/.gitignore @@ -376,4 +376,7 @@ _site/ JSON/ # Mac DS_Store -.DS_Store \ No newline at end of file +.DS_Store + +# Code Rush +/EXILED/.cr/* From 976f6f5aaa343ca70f2aef768fe66e3e386d148e Mon Sep 17 00:00:00 2001 From: Valentin Arthur Thomas Date: Mon, 3 Feb 2025 18:06:33 +0100 Subject: [PATCH 3/3] remove comments --- EXILED/Exiled.Events/Features/Event.cs | 13 ++++++------- EXILED/Exiled.Events/Features/Event{T}.cs | 11 ++++++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/EXILED/Exiled.Events/Features/Event.cs b/EXILED/Exiled.Events/Features/Event.cs index b06f3117e..c6c398b15 100644 --- a/EXILED/Exiled.Events/Features/Event.cs +++ b/EXILED/Exiled.Events/Features/Event.cs @@ -116,8 +116,6 @@ public Event() public void Subscribe(CustomEventHandler handler) => Subscribe(handler, 0); - // I try to do inheritated summary but the analizer refuse - /// /// Subscribes a target to the inner event if the conditional is true. /// @@ -211,20 +209,19 @@ public void Unsubscribe(CustomAsyncEventHandler handler) public void InvokeSafely() { BlendedInvoke(); - - // InvokeNormal(); - // InvokeAsync(); } /// internal void BlendedInvoke() { - int count = innerEvent.Count + innerAsyncEvent.Count; + Registration[] innerEvent = this.innerEvent.ToArray(); + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + int count = innerEvent.Length + innerAsyncEvent.Length; int eventIndex = 0, asyncEventIndex = 0; for (int i = 0; i < count; i++) { - if (eventIndex < innerEvent.Count && (asyncEventIndex >= innerAsyncEvent.Count || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) + if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) { try { @@ -256,6 +253,7 @@ internal void BlendedInvoke() /// internal void InvokeNormal() { + Registration[] innerEvent = this.innerEvent.ToArray(); foreach (Registration registration in innerEvent) { try @@ -272,6 +270,7 @@ internal void InvokeNormal() /// internal void InvokeAsync() { + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); foreach (AsyncRegistration registration in innerAsyncEvent) { try diff --git a/EXILED/Exiled.Events/Features/Event{T}.cs b/EXILED/Exiled.Events/Features/Event{T}.cs index 7150316b5..8604998ee 100644 --- a/EXILED/Exiled.Events/Features/Event{T}.cs +++ b/EXILED/Exiled.Events/Features/Event{T}.cs @@ -215,20 +215,19 @@ public void Unsubscribe(CustomAsyncEventHandler handler) public void InvokeSafely(T arg) { BlendedInvoke(arg); - - // InvokeNormal(arg); - // InvokeAsync(arg); } /// internal void BlendedInvoke(T arg) { - int count = innerEvent.Count + innerAsyncEvent.Count; + Registration[] innerEvent = this.innerEvent.ToArray(); + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + int count = innerEvent.Length + innerAsyncEvent.Length; int eventIndex = 0, asyncEventIndex = 0; for (int i = 0; i < count; i++) { - if (eventIndex < innerEvent.Count && (asyncEventIndex >= innerAsyncEvent.Count || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) + if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || innerEvent[eventIndex].priority >= innerAsyncEvent[asyncEventIndex].priority)) { try { @@ -260,6 +259,7 @@ internal void BlendedInvoke(T arg) /// internal void InvokeNormal(T arg) { + Registration[] innerEvent = this.innerEvent.ToArray(); foreach (Registration registration in innerEvent) { try @@ -276,6 +276,7 @@ internal void InvokeNormal(T arg) /// internal void InvokeAsync(T arg) { + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); foreach (AsyncRegistration registration in innerAsyncEvent) { try