//#define EVENTROUTER_THROWEXCEPTIONS
#if EVENTROUTER_THROWEXCEPTIONS
//#define EVENTROUTER_REQUIRELISTENER // Uncomment this if you want listeners to be required for sending events.
#endif
using System;
using UnityEngine;
using UnityEngine.Events;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Audio;
namespace MoreMountains.Tools
{
///
/// MMGameEvents are used throughout the game for general game events (game started, game ended, life lost, etc.)
///
public struct MMGameEvent
{
public string EventName;
public MMGameEvent(string newName)
{
EventName = newName;
}
static MMGameEvent e;
public static void Trigger(string newName)
{
e.EventName = newName;
MMEventManager.TriggerEvent(e);
}
}
///
/// This class handles event management, and can be used to broadcast events throughout the game, to tell one class (or many) that something's happened.
/// Events are structs, you can define any kind of events you want. This manager comes with MMGameEvents, which are
/// basically just made of a string, but you can work with more complex ones if you want.
///
/// To trigger a new event, from anywhere, do YOUR_EVENT.Trigger(YOUR_PARAMETERS)
/// So MMGameEvent.Trigger("Save"); for example will trigger a Save MMGameEvent
///
/// you can also call MMEventManager.TriggerEvent(YOUR_EVENT);
/// For example : MMEventManager.TriggerEvent(new MMGameEvent("GameStart")); will broadcast an MMGameEvent named GameStart to all listeners.
///
/// To start listening to an event from any class, there are 3 things you must do :
///
/// 1 - tell that your class implements the MMEventListener interface for that kind of event.
/// For example: public class GUIManager : Singleton, MMEventListener
/// You can have more than one of these (one per event type).
///
/// 2 - On Enable and Disable, respectively start and stop listening to the event :
/// void OnEnable()
/// {
/// this.MMEventStartListening();
/// }
/// void OnDisable()
/// {
/// this.MMEventStopListening();
/// }
///
/// 3 - Implement the MMEventListener interface for that event. For example :
/// public void OnMMEvent(MMGameEvent gameEvent)
/// {
/// if (gameEvent.EventName == "GameOver")
/// {
/// // DO SOMETHING
/// }
/// }
/// will catch all events of type MMGameEvent emitted from anywhere in the game, and do something if it's named GameOver
///
[ExecuteAlways]
public static class MMEventManager
{
private static Dictionary> _subscribersList;
static MMEventManager()
{
_subscribersList = new Dictionary>();
}
///
/// Adds a new subscriber to a certain event.
///
/// listener.
/// The event type.
public static void AddListener( MMEventListener listener ) where MMEvent : struct
{
Type eventType = typeof( MMEvent );
if (!_subscribersList.ContainsKey(eventType))
{
_subscribersList[eventType] = new List();
}
if (!SubscriptionExists(eventType, listener))
{
_subscribersList[eventType].Add( listener );
}
}
///
/// Removes a subscriber from a certain event.
///
/// listener.
/// The event type.
public static void RemoveListener( MMEventListener listener ) where MMEvent : struct
{
Type eventType = typeof( MMEvent );
if( !_subscribersList.ContainsKey( eventType ) )
{
#if EVENTROUTER_THROWEXCEPTIONS
throw new ArgumentException( string.Format( "Removing listener \"{0}\", but the event type \"{1}\" isn't registered.", listener, eventType.ToString() ) );
#else
return;
#endif
}
List subscriberList = _subscribersList[eventType];
#if EVENTROUTER_THROWEXCEPTIONS
bool listenerFound = false;
#endif
for (int i = subscriberList.Count-1; i >= 0; i--)
{
if( subscriberList[i] == listener )
{
subscriberList.Remove( subscriberList[i] );
#if EVENTROUTER_THROWEXCEPTIONS
listenerFound = true;
#endif
if ( subscriberList.Count == 0 )
{
_subscribersList.Remove(eventType);
}
return;
}
}
#if EVENTROUTER_THROWEXCEPTIONS
if( !listenerFound )
{
throw new ArgumentException( string.Format( "Removing listener, but the supplied receiver isn't subscribed to event type \"{0}\".", eventType.ToString() ) );
}
#endif
}
///
/// Triggers an event. All instances that are subscribed to it will receive it (and will potentially act on it).
///
/// The event to trigger.
/// The 1st type parameter.
public static void TriggerEvent( MMEvent newEvent ) where MMEvent : struct
{
List list;
if( !_subscribersList.TryGetValue( typeof( MMEvent ), out list ) )
#if EVENTROUTER_REQUIRELISTENER
throw new ArgumentException( string.Format( "Attempting to send event of type \"{0}\", but no listener for this type has been found. Make sure this.Subscribe<{0}>(EventRouter) has been called, or that all listeners to this event haven't been unsubscribed.", typeof( MMEvent ).ToString() ) );
#else
return;
#endif
for (int i=list.Count-1; i >= 0; i--)
{
( list[i] as MMEventListener ).OnMMEvent( newEvent );
}
}
///
/// Checks if there are subscribers for a certain type of events
///
/// true, if exists was subscriptioned, false otherwise.
/// Type.
/// Receiver.
private static bool SubscriptionExists( Type type, MMEventListenerBase receiver )
{
List receivers;
if( !_subscribersList.TryGetValue( type, out receivers ) ) return false;
bool exists = false;
for (int i = receivers.Count-1; i >= 0; i--)
{
if( receivers[i] == receiver )
{
exists = true;
break;
}
}
return exists;
}
}
///
/// Static class that allows any class to start or stop listening to events
///
public static class EventRegister
{
public delegate void Delegate( T eventType );
public static void MMEventStartListening( this MMEventListener caller ) where EventType : struct
{
MMEventManager.AddListener( caller );
}
public static void MMEventStopListening( this MMEventListener caller ) where EventType : struct
{
MMEventManager.RemoveListener( caller );
}
}
///
/// Event listener basic interface
///
public interface MMEventListenerBase { };
///
/// A public interface you'll need to implement for each type of event you want to listen to.
///
public interface MMEventListener : MMEventListenerBase
{
void OnMMEvent( T eventType );
}
public class MMEventListenerWrapper : MMEventListener, IDisposable
where TEvent : struct
{
private Action _callback;
private TOwner _owner;
public MMEventListenerWrapper(TOwner owner, Action callback)
{
_owner = owner;
_callback = callback;
RegisterCallbacks(true);
}
public void Dispose()
{
RegisterCallbacks(false);
_callback = null;
}
protected virtual TTarget OnEvent(TEvent eventType) => default;
public void OnMMEvent(TEvent eventType)
{
var item = OnEvent(eventType);
_callback?.Invoke(item);
}
private void RegisterCallbacks(bool b)
{
if (b)
{
this.MMEventStartListening();
}
else
{
this.MMEventStopListening();
}
}
}
}