Skip to content

Commit

Permalink
feat: create new c# only events
Browse files Browse the repository at this point in the history
events have placeholder names and will be renamed with breaking change later
  • Loading branch information
James-Frowen committed Mar 25, 2024
1 parent e74d7dd commit 1c9f2d8
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 44 deletions.
63 changes: 36 additions & 27 deletions Assets/Mirage/Runtime/Events/AddLateEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,14 @@ namespace Mirage.Events
/// </code>
/// </example>
[Serializable]
public sealed class AddLateEvent : AddLateEventBase, IAddLateEvent
public sealed class AddLateEvent : AddLateEvent_new, IAddLateEvent
{
[SerializeField] private UnityEvent _event = new UnityEvent();

protected override UnityEventBase baseEvent => _event;

public void AddListener(UnityAction handler)
{
// invoke handler if event has been invoked atleast once
if (hasInvoked)
if (HasInvoked)
{
handler.Invoke();
}
Expand All @@ -89,12 +87,19 @@ public void RemoveListener(UnityAction handler)
_event.RemoveListener(handler);
}

public void Invoke()
public override void Invoke()
{
MarkInvoked();

base.Invoke();
_event.Invoke();
}

/// <summary>
/// Remove all non-persisent (ie created from script) listeners from the event.
/// </summary>
public void RemoveAllListeners()
{
_event.RemoveAllListeners();
}
}

/// <summary>
Expand All @@ -104,18 +109,15 @@ public void Invoke()
/// <typeparam name="T0">argument 0</typeparam>
/// <typeparam name="TEvent">UnityEvent</typeparam>
[Serializable]
public abstract class AddLateEvent<T0, TEvent> : AddLateEventBase, IAddLateEvent<T0>
public abstract class AddLateEvent<T0, TEvent> : AddLateEvent_new<T0>, IAddLateEvent<T0>
where TEvent : UnityEvent<T0>, new()
{
[SerializeField] private TEvent _event = new TEvent();
protected override UnityEventBase baseEvent => _event;

private T0 _arg0;

public void AddListener(UnityAction<T0> handler)
{
// invoke handler if event has been invoked atleast once
if (hasInvoked)
if (HasInvoked)
{
handler.Invoke(_arg0);
}
Expand All @@ -129,13 +131,19 @@ public void RemoveListener(UnityAction<T0> handler)
_event.RemoveListener(handler);
}

public void Invoke(T0 arg0)
public override void Invoke(T0 arg0)
{
MarkInvoked();

_arg0 = arg0;
base.Invoke(arg0);
_event.Invoke(arg0);
}

/// <summary>
/// Remove all non-persisent (ie created from script) listeners from the event.
/// </summary>
public void RemoveAllListeners()
{
_event.RemoveAllListeners();
}
}

/// <summary>
Expand All @@ -145,19 +153,15 @@ public void Invoke(T0 arg0)
/// <typeparam name="T0"></typeparam>
/// <typeparam name="T1"></typeparam>
[Serializable]
public abstract class AddLateEvent<T0, T1, TEvent> : AddLateEventBase, IAddLateEvent<T0, T1>
public abstract class AddLateEvent<T0, T1, TEvent> : AddLateEvent_new<T0, T1>, IAddLateEvent<T0, T1>
where TEvent : UnityEvent<T0, T1>, new()
{
[SerializeField] private TEvent _event = new TEvent();
protected override UnityEventBase baseEvent => _event;

private T0 _arg0;
private T1 _arg1;

public void AddListener(UnityAction<T0, T1> handler)
{
// invoke handler if event has been invoked atleast once
if (hasInvoked)
if (HasInvoked)
{
handler.Invoke(_arg0, _arg1);
}
Expand All @@ -171,13 +175,18 @@ public void RemoveListener(UnityAction<T0, T1> handler)
_event.RemoveListener(handler);
}

public void Invoke(T0 arg0, T1 arg1)
public override void Invoke(T0 arg0, T1 arg1)
{
MarkInvoked();

_arg0 = arg0;
_arg1 = arg1;
base.Invoke(arg0, arg1);
_event.Invoke(arg0, arg1);
}

/// <summary>
/// Remove all non-persisent (ie created from script) listeners from the event.
/// </summary>
public void RemoveAllListeners()
{
_event.RemoveAllListeners();
}
}
}
17 changes: 3 additions & 14 deletions Assets/Mirage/Runtime/Events/AddLateEventBase.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using UnityEngine.Events;

namespace Mirage.Events
{
public abstract class AddLateEventBase
{
protected abstract UnityEventBase baseEvent { get; }
protected bool hasInvoked { get; private set; }
protected bool HasInvoked;

protected void MarkInvoked()
{
hasInvoked = true;
HasInvoked = true;
}

/// <summary>
Expand All @@ -18,15 +15,7 @@ protected void MarkInvoked()
/// </summary>
public void Reset()
{
hasInvoked = false;
}

/// <summary>
/// Remove all non-persisent (ie created from script) listeners from the event.
/// </summary>
public void RemoveAllListeners()
{
baseEvent.RemoveAllListeners();
HasInvoked = false;
}
}
}
194 changes: 194 additions & 0 deletions Assets/Mirage/Runtime/Events/AddLateEvent_new.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using UnityEngine.Events;

namespace Mirage.Events
{
/// <summary>
/// An event that will invoke handlers immediately if they are added after <see cref="Invoke"/> has been called
/// </summary>
/// <remarks>
/// <para>
/// AddLateEvent should be used for time sensitive events where Invoke might be called before the user has chance to add a handler.
/// For example Server Started event.
/// </para>
/// <para>
/// Events that are invoked multiple times, like AuthorityChanged, will have the most recent <see cref="Invoke"/> argument sent to new handler.
/// </para>
/// </remarks>
/// <example>
/// This Example shows uses of Event
/// <code>
///
/// public class Server : MonoBehaviour
/// {
/// // shows in inspector
/// [SerializeField]
/// private AddLateEvent _started;
///
/// // expose interface so others can add handlers, but does not let them invoke
/// public IAddLateEvent Started => customEvent;
///
/// public void StartServer()
/// {
/// // ...
///
/// // invoke using field
/// _started.Invoke();
/// }
///
/// public void StopServer()
/// {
/// // ...
///
/// // reset event, resets the hasInvoked flag
/// _started.Reset();
/// }
/// }
/// </code>
/// </example>
/// <example>
/// This is an example to show how to create events with arguments:
/// <code>
/// // Serializable so that it can be used in inspector
/// [Serializable]
/// public class IntUnityEvent : UnityEvent&lt;int&gt; { }
/// [Serializable]
/// public class IntAddLateEvent : AddLateEvent&lt;int, IntUnityEvent&gt; { }
///
/// public class MyClass : MonoBehaviour
/// {
/// [SerializeField]
/// private IntAddLateEvent customEvent;
///
/// public IAddLateEvent&lt;int&gt; CustomEvent => customEvent;
/// }
/// </code>
/// </example>
[Serializable]
public class AddLateEvent_new : AddLateEventBase, IAddLateEvent_new
{
private static readonly List<Action> tmp = new List<Action>();
private readonly List<Action> _listeners = new List<Action>();

public void AddListener(Action handler)
{
// invoke handler if event has been invoked atleast once
if (HasInvoked)
{
handler.Invoke();
}

// add handler to inner event so that it can be invoked again
_listeners.Add(handler);
}
public void RemoveListener(Action handler)
{
_listeners.Remove(handler);
}

public virtual void Invoke()
{
MarkInvoked();

// tmp incase RemoveListener is called inside loop
tmp.Clear();
tmp.AddRange(_listeners);
foreach (var handler in tmp)
handler.Invoke();
tmp.Clear();
}
}

/// <summary>
/// Version of <see cref="AddLateEvent_new"/> with 1 argument
/// <para>Create a non-generic class inheriting from this to use in inspector. Same rules as <see cref="UnityEvent"/></para>
/// </summary>
/// <typeparam name="T0">argument 0</typeparam>
/// <typeparam name="TEvent">UnityEvent</typeparam>
[Serializable]
public abstract class AddLateEvent_new<T0> : AddLateEventBase, IAddLateEvent_new<T0>
{
private static readonly List<Action<T0>> tmp = new List<Action<T0>>();
private readonly List<Action<T0>> _listeners = new List<Action<T0>>();

protected T0 _arg0;

public void AddListener(Action<T0> handler)
{
// invoke handler if event has been invoked atleast once
if (HasInvoked)
{
handler.Invoke(_arg0);
}

// add handler to inner event so that it can be invoked again
_listeners.Add(handler);
}

public void RemoveListener(Action<T0> handler)
{
_listeners.Remove(handler);
}

public virtual void Invoke(T0 arg0)
{
MarkInvoked();

_arg0 = arg0;
// tmp incase RemoveListener is called inside loop
tmp.Clear();
tmp.AddRange(_listeners);
foreach (var handler in tmp)
handler.Invoke(arg0);
tmp.Clear();
}
}

/// <summary>
/// Version of <see cref="AddLateEvent_new"/> with 2 arguments
/// <para>Create a non-generic class inheriting from this to use in inspector. Same rules as <see cref="UnityEvent"/></para>
/// </summary>
/// <typeparam name="T0"></typeparam>
/// <typeparam name="T1"></typeparam>
[Serializable]
public abstract class AddLateEvent_new<T0, T1> : AddLateEventBase, IAddLateEvent_new<T0, T1>
{
private static readonly List<Action<T0, T1>> tmp = new List<Action<T0, T1>>();
private readonly List<Action<T0, T1>> _listeners = new List<Action<T0, T1>>();

protected T0 _arg0;
protected T1 _arg1;

public void AddListener(Action<T0, T1> handler)
{
// invoke handler if event has been invoked atleast once
if (HasInvoked)
{
handler.Invoke(_arg0, _arg1);
}

// add handler to inner event so that it can be invoked again
_listeners.Add(handler);
}

public void RemoveListener(Action<T0, T1> handler)
{
_listeners.Remove(handler);
}

public virtual void Invoke(T0 arg0, T1 arg1)
{
MarkInvoked();

_arg0 = arg0;
_arg1 = arg1;
// tmp incase RemoveListener is called inside loop
tmp.Clear();
tmp.AddRange(_listeners);
foreach (var handler in tmp)
handler.Invoke(arg0, arg1);
tmp.Clear();
}
}
}
11 changes: 11 additions & 0 deletions Assets/Mirage/Runtime/Events/AddLateEvent_new.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 1c9f2d8

Please sign in to comment.