diff --git a/.gitignore b/.gitignore index 958e3428c7..722b747e7c 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/* diff --git a/EXILED/Exiled.Events/Features/Event.cs b/EXILED/Exiled.Events/Features/Event.cs index 9885747c7e..fc5c0c4333 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,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!"); @@ -114,7 +131,21 @@ public void Subscribe(CustomEventHandler handler) patched = true; } - InnerEvent += handler; + if (handler == null) + return; + + 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 +153,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 +170,21 @@ public void Subscribe(CustomAsyncEventHandler handler) patched = true; } - InnerAsyncEvent += handler; + if (handler == null) + return; + + 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 +193,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 +204,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 +214,61 @@ public void Unsubscribe(CustomAsyncEventHandler handler) /// public void InvokeSafely() { - InvokeNormal(); - InvokeAsync(); + BlendedInvoke(); } /// - internal void InvokeNormal() + internal void BlendedInvoke() { - if (InnerEvent is null) - return; + Registration[] innerEvent = this.innerEvent.ToArray(); + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + int count = innerEvent.Length + innerAsyncEvent.Length; + int eventIndex = 0, asyncEventIndex = 0; - foreach (CustomEventHandler handler in InnerEvent.GetInvocationList().Cast()) + for (int i = 0; i < count; i++) + { + if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || 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() + { + Registration[] innerEvent = this.innerEvent.ToArray(); + 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 +276,16 @@ internal void InvokeNormal() /// internal void InvokeAsync() { - if (InnerAsyncEvent is null) - return; - - foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast()) + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + 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 e9b6e546b9..2d6252b91e 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,21 @@ public void Subscribe(CustomEventHandler handler) patched = true; } - InnerEvent += handler; + if (handler == null) + return; + + 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 +157,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 +174,21 @@ public void Subscribe(CustomAsyncEventHandler handler) patched = true; } - InnerAsyncEvent += handler; + if (handler == null) + return; + + 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 +197,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 +208,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 +220,61 @@ public void Unsubscribe(CustomAsyncEventHandler handler) /// Event or its arg is . public void InvokeSafely(T arg) { - InvokeNormal(arg); - InvokeAsync(arg); + BlendedInvoke(arg); } /// - internal void InvokeNormal(T arg) + internal void BlendedInvoke(T arg) { - if (InnerEvent is null) - return; + Registration[] innerEvent = this.innerEvent.ToArray(); + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + int count = innerEvent.Length + innerAsyncEvent.Length; + int eventIndex = 0, asyncEventIndex = 0; - foreach (CustomEventHandler handler in InnerEvent.GetInvocationList().Cast>()) + for (int i = 0; i < count; i++) + { + if (eventIndex < innerEvent.Length && (asyncEventIndex >= innerAsyncEvent.Length || 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) + { + Registration[] innerEvent = this.innerEvent.ToArray(); + 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 +282,16 @@ internal void InvokeNormal(T arg) /// internal void InvokeAsync(T arg) { - if (InnerAsyncEvent is null) - return; - - foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast>()) + AsyncRegistration[] innerAsyncEvent = this.innerAsyncEvent.ToArray(); + 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}"); } } }