Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,7 @@ _site/
JSON/

# Mac DS_Store
.DS_Store
.DS_Store

# Code Rush
/EXILED/.cr/*
129 changes: 107 additions & 22 deletions EXILED/Exiled.Events/Features/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Exiled.Events.Features
using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MEC;
using PluginAPI.Roles;

/// <summary>
/// The custom <see cref="EventHandler"/> delegate, with empty parameters.
Expand All @@ -31,8 +32,20 @@ namespace Exiled.Events.Features
/// </summary>
public class Event : IExiledEvent
{
private record Registration(CustomEventHandler handler, int priority);

private record AsyncRegistration(CustomAsyncEventHandler handler, int priority);

private static readonly List<Event> EventsValue = new();

private static readonly IComparer<Registration> RegisterComparable = Comparer<Registration>.Create((x, y) => y.priority - x.priority);

private static readonly IComparer<AsyncRegistration> AsyncRegisterComparable = Comparer<AsyncRegistration>.Create((x, y) => y.priority - x.priority);

private readonly List<Registration> innerEvent = new();

private readonly List<AsyncRegistration> innerAsyncEvent = new();

private bool patched;

/// <summary>
Expand All @@ -43,10 +56,6 @@ public Event()
EventsValue.Add(this);
}

private event CustomEventHandler InnerEvent;

private event CustomAsyncEventHandler InnerAsyncEvent;

/// <summary>
/// Gets a <see cref="IReadOnlyList{T}"/> of <see cref="Event{T}"/> which contains all the <see cref="Event{T}"/> instances.
/// </summary>
Expand Down Expand Up @@ -105,6 +114,14 @@ public Event()
/// </summary>
/// <param name="handler">The handler to add.</param>
public void Subscribe(CustomEventHandler handler)
=> Subscribe(handler, 0);

/// <summary>
/// Subscribes a target <see cref="CustomEventHandler"/> to the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
/// <param name="priority">The highest priority is the first called, the lowest the last.</param>
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!");

Expand All @@ -114,14 +131,33 @@ 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);
}
}

/// <summary>
/// Subscribes a target <see cref="CustomAsyncEventHandler"/> to the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
public void Subscribe(CustomAsyncEventHandler handler)
=> Subscribe(handler, 0);

/// <summary>
/// Subscribes a target <see cref="CustomAsyncEventHandler"/> to the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
/// <param name="priority">The highest priority is the first called, the lowest the last.</param>
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!");

Expand All @@ -131,7 +167,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);
}
}

/// <summary>
Expand All @@ -140,7 +187,9 @@ public void Subscribe(CustomAsyncEventHandler handler)
/// <param name="handler">The handler to add.</param>
public void Unsubscribe(CustomEventHandler handler)
{
InnerEvent -= handler;
int index = innerEvent.FindIndex(p => p.handler == handler);
if (index != -1)
innerEvent.RemoveAt(index);
}

/// <summary>
Expand All @@ -149,52 +198,88 @@ public void Unsubscribe(CustomEventHandler handler)
/// <param name="handler">The handler to add.</param>
public void Unsubscribe(CustomAsyncEventHandler handler)
{
InnerAsyncEvent -= handler;
int index = innerAsyncEvent.FindIndex(p => p.handler == handler);
if (index != -1)
innerAsyncEvent.RemoveAt(index);
}

/// <summary>
/// Executes all <see cref="CustomEventHandler"/> listeners safely.
/// </summary>
public void InvokeSafely()
{
InvokeNormal();
InvokeAsync();
BlendedInvoke();
}

/// <inheritdoc cref="InvokeSafely"/>
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;

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++;
}
}
}

foreach (CustomEventHandler handler in InnerEvent.GetInvocationList().Cast<CustomEventHandler>())
/// <inheritdoc cref="InvokeSafely"/>
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}");
}
}
}

/// <inheritdoc cref="InvokeSafely"/>
internal void InvokeAsync()
{
if (InnerAsyncEvent is null)
return;

foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast<CustomAsyncEventHandler>())
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}");
}
}
}
Expand Down
Loading