Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inroducing... "Async" events #2303

Merged
merged 11 commits into from
Dec 5, 2023
85 changes: 85 additions & 0 deletions Exiled.Events/Features/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ namespace Exiled.Events.Features

using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MEC;

/// <summary>
/// The custom <see cref="EventHandler"/> delegate, with empty parameters.
/// </summary>
public delegate void CustomEventHandler();

/// <summary>
/// THe custom <see cref="EventHandler"/> delegate, with empty parameters. Holds async events with <see cref="MEC"/>.
/// </summary>
/// <returns><see cref="IEnumerator{T}"/> of <see cref="float"/>.</returns>
public delegate IEnumerator<float> CustomAsyncEventHandler();

/// <summary>
/// An implementation of <see cref="IExiledEvent"/> that encapsulates a no-argument event.
/// </summary>
Expand All @@ -38,6 +45,8 @@ public Event()

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 All @@ -55,6 +64,18 @@ public Event()
return @event;
}

/// <summary>
/// Subscribes a <see cref="CustomAsyncEventHandler"/> to the inner event, and checks patches if dynamic patching is enabled.
/// </summary>
/// <param name="event">The <see cref="Event"/> to subscribe the <see cref="CustomAsyncEventHandler"/> to.</param>
/// <param name="asyncEventHandler">The <see cref="CustomAsyncEventHandler"/> to subscribe to the <see cref="Event"/>.</param>
/// <returns>The <see cref="Event"/> with the handler added to it.</returns>
public static Event operator +(Event @event, CustomAsyncEventHandler asyncEventHandler)
{
@event.Subscribe(asyncEventHandler);
return @event;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomEventHandler"/> from the inner event, and checks if unpatching is possible, if dynamic patching is enabled.
/// </summary>
Expand All @@ -67,6 +88,18 @@ public Event()
return @event;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomAsyncEventHandler"/> from the inner event, and checks if unpatching is possible, if dynamic patching is enabled.
/// </summary>
/// <param name="event">The <see cref="Event"/> the <see cref="CustomAsyncEventHandler"/> will be unsubscribed from.</param>
/// <param name="asyncEventHandler">The <see cref="CustomAsyncEventHandler"/> that will be unsubscribed from the <see cref="Event"/>.</param>
/// <returns>The <see cref="Event"/> with the handler unsubscribed from it.</returns>
public static Event operator -(Event @event, CustomAsyncEventHandler asyncEventHandler)
{
@event.Unsubscribe(asyncEventHandler);
return @event;
}

/// <summary>
/// Subscribes a target <see cref="CustomEventHandler"/> to the inner event if the conditional is true.
/// </summary>
Expand All @@ -84,6 +117,23 @@ public void Subscribe(CustomEventHandler handler)
InnerEvent += handler;
}

/// <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)
{
Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!");

if (Events.Instance.Config.UseDynamicPatching && !patched)
{
Events.Instance.Patcher.Patch(this);
patched = true;
}

InnerAsyncEvent += handler;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomEventHandler"/> from the inner event if the conditional is true.
/// </summary>
Expand All @@ -93,10 +143,26 @@ public void Unsubscribe(CustomEventHandler handler)
InnerEvent -= handler;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomAsyncEventHandler"/> from the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
public void Unsubscribe(CustomAsyncEventHandler handler)
{
InnerAsyncEvent -= handler;
}

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

/// <inheritdoc cref="InvokeSafely"/>
internal void InvokeNormal()
{
if (InnerEvent is null)
return;
Expand All @@ -113,5 +179,24 @@ public void InvokeSafely()
}
}
}

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

foreach (CustomAsyncEventHandler handler in InnerAsyncEvent.GetInvocationList().Cast<CustomAsyncEventHandler>())
{
try
{
Timing.RunCoroutine(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}");
}
}
}
}
}
87 changes: 87 additions & 0 deletions Exiled.Events/Features/Event{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ namespace Exiled.Events.Features

using Exiled.API.Features;
using Exiled.Events.EventArgs.Interfaces;
using MEC;

/// <summary>
/// The custom <see cref="EventHandler"/> delegate.
Expand All @@ -21,6 +22,14 @@ namespace Exiled.Events.Features
/// <param name="ev">The <see cref="EventHandler{TEventArgs}"/> instance.</param>
public delegate void CustomEventHandler<TEventArgs>(TEventArgs ev);

/// <summary>
/// The custom <see cref="EventHandler"/> delegate.
/// </summary>
/// <typeparam name="TEventArgs">The <see cref="EventHandler{TEventArgs}"/> type.</typeparam>
/// <param name="ev">The <see cref="EventHandler{TEventArgs}"/> instance.</param>
/// <returns><see cref="IEnumerator{T}"/> of <see cref="float"/>.</returns>
public delegate IEnumerator<float> CustomAsyncEventHandler<TEventArgs>(TEventArgs ev);

/// <summary>
/// An implementation of the <see cref="IExiledEvent"/> interface that encapsulates an event with arguments.
/// </summary>
Expand All @@ -41,6 +50,8 @@ public Event()

private event CustomEventHandler<T> InnerEvent;

private event CustomAsyncEventHandler<T> InnerAsyncEvent;

/// <summary>
/// Gets a <see cref="IReadOnlyCollection{T}"/> of <see cref="Event{T}"/> which contains all the <see cref="Event{T}"/> instances.
/// </summary>
Expand All @@ -58,6 +69,18 @@ public Event()
return @event;
}

/// <summary>
/// Subscribes a <see cref="CustomAsyncEventHandler"/> to the inner event, and checks patches if dynamic patching is enabled.
/// </summary>
/// <param name="event">The <see cref="Event{T}"/> to subscribe the <see cref="CustomAsyncEventHandler{T}"/> to.</param>
/// <param name="asyncEventHandler">The <see cref="CustomAsyncEventHandler{T}"/> to subscribe to the <see cref="Event{T}"/>.</param>
/// <returns>The <see cref="Event{T}"/> with the handler added to it.</returns>
public static Event<T> operator +(Event<T> @event, CustomAsyncEventHandler<T> asyncEventHandler)
{
@event.Subscribe(asyncEventHandler);
return @event;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomEventHandler{TEventArgs}"/> from the inner event and checks if unpatching is possible, if dynamic patching is enabled.
/// </summary>
Expand All @@ -70,6 +93,18 @@ public Event()
return @event;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomAsyncEventHandler{TEventArgs}"/> from the inner event, and checks if unpatching is possible, if dynamic patching is enabled.
/// </summary>
/// <param name="event">The <see cref="Event"/> the <see cref="CustomAsyncEventHandler{T}"/> will be unsubscribed from.</param>
/// <param name="asyncEventHandler">The <see cref="CustomAsyncEventHandler{T}"/> that will be unsubscribed from the <see cref="Event{T}"/>.</param>
/// <returns>The <see cref="Event{T}"/> with the handler unsubscribed from it.</returns>
public static Event<T> operator -(Event<T> @event, CustomAsyncEventHandler<T> asyncEventHandler)
{
@event.Unsubscribe(asyncEventHandler);
return @event;
}

/// <summary>
/// Subscribes a target <see cref="CustomEventHandler{T}"/> to the inner event if the conditional is true.
/// </summary>
Expand All @@ -87,6 +122,23 @@ public void Subscribe(CustomEventHandler<T> handler)
InnerEvent += handler;
}

/// <summary>
/// Subscribes a target <see cref="CustomAsyncEventHandler{T}"/> to the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
public void Subscribe(CustomAsyncEventHandler<T> handler)
{
Log.Assert(Events.Instance is not null, $"{nameof(Events.Instance)} is null, please ensure you have exiled_events enabled!");

if (Events.Instance.Config.UseDynamicPatching && !patched)
{
Events.Instance.Patcher.Patch(this);
patched = true;
}

InnerAsyncEvent += handler;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomEventHandler{T}"/> from the inner event if the conditional is true.
/// </summary>
Expand All @@ -96,12 +148,28 @@ public void Unsubscribe(CustomEventHandler<T> handler)
InnerEvent -= handler;
}

/// <summary>
/// Unsubscribes a target <see cref="CustomEventHandler{T}"/> from the inner event if the conditional is true.
/// </summary>
/// <param name="handler">The handler to add.</param>
public void Unsubscribe(CustomAsyncEventHandler<T> handler)
{
InnerAsyncEvent -= handler;
}

/// <summary>
/// Executes all <see cref="CustomEventHandler{TEventArgs}"/> listeners safely.
/// </summary>
/// <param name="arg">The event argument.</param>
/// <exception cref="ArgumentNullException">Event or its arg is <see langword="null"/>.</exception>
public void InvokeSafely(T arg)
{
InvokeNormal(arg);
InvokeAsync(arg);
}

/// <inheritdoc cref="InvokeSafely"/>
internal void InvokeNormal(T arg)
{
if (InnerEvent is null)
return;
louis1706 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -118,5 +186,24 @@ public void InvokeSafely(T arg)
}
}
}

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

foreach (CustomAsyncEventHandler<T> handler in InnerAsyncEvent.GetInvocationList().Cast<CustomAsyncEventHandler<T>>())
{
try
{
Timing.RunCoroutine(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}");
}
}
}
}
}
Loading