/*
* Advanced C# messenger by Ilya Suzdalnitski. V1.0
*
* Based on Rod Hyde's "CSharpMessenger" and Magnus Wolffelt's "CSharpMessenger Extended".
*
* Features:
* Prevents a MissingReferenceException because of a reference to a destroyed message handler.
* Option to log all messages
* Extensive error detection, preventing silent bugs
*
* Usage examples:
1. Messenger.AddListener<GameObject>("prop collected", PropCollected);
Messenger.Broadcast<GameObject>("prop collected", prop);
2. Messenger.AddListener<float>("speed changed", SpeedChanged);
Messenger.Broadcast<float>("speed changed", 0.5f);
*
* Messenger cleans up its evenTable automatically upon loading of a new level.
*
* Don't forget that the messages that should survive the cleanup, should be marked with Messenger.MarkAsPermanent(string)
*
*/
//#define LOG_ALL_MESSAGES
//#define LOG_ADD_LISTENER
//#define LOG_BROADCAST_MESSAGE
#define REQUIRE_LISTENER
usingSystem;
usingSystem.Collections.Generic;
usingUnityEngine;
staticinternalclassMessenger{
#region Internal variables
//Disable the unused variable warning
#pragma warning disable 0414
//Ensures that the MessengerHelper will be created automatically upon start of the game.
staticprivateMessengerHelpermessengerHelper=(newGameObject("MessengerHelper")).AddComponent<MessengerHelper>();
#pragma warning restore 0414
staticpublicDictionary<string,Delegate>eventTable=newDictionary<string,Delegate>();
//Message handlers that should never be removed, regardless of calling Cleanup
staticpublicList<string>permanentMessages=newList<string>();
#endregion
#region Helper methods
//Marks a certain message as permanent.
staticpublicvoidMarkAsPermanent(stringeventType){
#if LOG_ALL_MESSAGES
Debug.Log("Messenger MarkAsPermanent \t\""+eventType+"\"");
#endif
permanentMessages.Add(eventType);
}
staticpublicvoidCleanup()
{
#if LOG_ALL_MESSAGES
Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed.");
#endif
List<string>messagesToRemove=newList<string>();
foreach(KeyValuePair<string,Delegate>pairineventTable){
boolwasFound=false;
foreach(stringmessageinpermanentMessages){
if(pair.Key==message){
wasFound=true;
break;
}
}
if(!wasFound)
messagesToRemove.Add(pair.Key);
}
foreach(stringmessageinmessagesToRemove){
eventTable.Remove(message);
}
}
staticpublicvoidPrintEventTable()
{
Debug.Log("\t\t\t=== MESSENGER PrintEventTable ===");
foreach(KeyValuePair<string,Delegate>pairineventTable){
Debug.Log("\t\t\t"+pair.Key+"\t\t"+pair.Value);
}
Debug.Log("\n");
}
#endregion
#region Message logging and exception throwing
staticpublicvoidOnListenerAdding(stringeventType,DelegatelistenerBeingAdded){
#if LOG_ALL_MESSAGES || LOG_ADD_LISTENER
Debug.Log("MESSENGER OnListenerAdding \t\""+eventType+"\"\t{"+listenerBeingAdded.Target+" -> "+listenerBeingAdded.Method+"}");
#endif
if(!eventTable.ContainsKey(eventType)){
eventTable.Add(eventType,null);
}
Delegated=eventTable[eventType];
if(d!=null&&d.GetType()!=listenerBeingAdded.GetType()){
thrownewListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}",eventType,d.GetType().Name,listenerBeingAdded.GetType().Name));
}
}
staticpublicvoidOnListenerRemoving(stringeventType,DelegatelistenerBeingRemoved){
#if LOG_ALL_MESSAGES
Debug.Log("MESSENGER OnListenerRemoving \t\""+eventType+"\"\t{"+listenerBeingRemoved.Target+" -> "+listenerBeingRemoved.Method+"}");
#endif
if(eventTable.ContainsKey(eventType)){
Delegated=eventTable[eventType];
if(d==null){
thrownewListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.",eventType));
}elseif(d.GetType()!=listenerBeingRemoved.GetType()){
thrownewListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}",eventType,d.GetType().Name,listenerBeingRemoved.GetType().Name));
}
}else{
thrownewListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.",eventType));
}
}
staticpublicvoidOnListenerRemoved(stringeventType){
if(eventTable[eventType]==null){
eventTable.Remove(eventType);
}
}
staticpublicvoidOnBroadcasting(stringeventType){
#if REQUIRE_LISTENER
if(!eventTable.ContainsKey(eventType)){
thrownewBroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.",eventType));
}
#endif
}
staticpublicBroadcastExceptionCreateBroadcastSignatureException(stringeventType){
returnnewBroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.",eventType));
}
publicclassBroadcastException:Exception{
publicBroadcastException(stringmsg)
:base(msg){
}
}
publicclassListenerException:Exception{
publicListenerException(stringmsg)
:base(msg){
}
}
#endregion
#region AddListener
//No parameters
staticpublicvoidAddListener(stringeventType,Callbackhandler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback)eventTable[eventType]+handler;
}
//Single parameter
staticpublicvoidAddListener<T>(stringeventType,Callback<T>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T>)eventTable[eventType]+handler;
}
//Two parameters
staticpublicvoidAddListener<T,U>(stringeventType,Callback<T,U>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T,U>)eventTable[eventType]+handler;
}
//Three parameters
staticpublicvoidAddListener<T,U,V>(stringeventType,Callback<T,U,V>handler){
OnListenerAdding(eventType,handler);
eventTable[eventType]=(Callback<T,U,V>)eventTable[eventType]+handler;
}
#endregion
#region RemoveListener
//No parameters
staticpublicvoidRemoveListener(stringeventType,Callbackhandler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Single parameter
staticpublicvoidRemoveListener<T>(stringeventType,Callback<T>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Two parameters
staticpublicvoidRemoveListener<T,U>(stringeventType,Callback<T,U>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T,U>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
//Three parameters
staticpublicvoidRemoveListener<T,U,V>(stringeventType,Callback<T,U,V>handler){
OnListenerRemoving(eventType,handler);
eventTable[eventType]=(Callback<T,U,V>)eventTable[eventType]-handler;
OnListenerRemoved(eventType);
}
#endregion
#region Broadcast
//No parameters
staticpublicvoidBroadcast(stringeventType){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking \t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callbackcallback=dasCallback;
if(callback!=null){
callback();
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Single parameter
staticpublicvoidBroadcast<T>(stringeventType,Targ1){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking \t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T>callback=dasCallback<T>;
if(callback!=null){
callback(arg1);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Two parameters
staticpublicvoidBroadcast<T,U>(stringeventType,Targ1,Uarg2){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking \t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T,U>callback=dasCallback<T,U>;
if(callback!=null){
callback(arg1,arg2);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
//Three parameters
staticpublicvoidBroadcast<T,U,V>(stringeventType,Targ1,Uarg2,Varg3){
#if LOG_ALL_MESSAGES || LOG_BROADCAST_MESSAGE
Debug.Log("MESSENGER\t"+System.DateTime.Now.ToString("hh:mm:ss.fff")+"\t\t\tInvoking \t\""+eventType+"\"");
#endif
OnBroadcasting(eventType);
Delegated;
if(eventTable.TryGetValue(eventType,outd)){
Callback<T,U,V>callback=dasCallback<T,U,V>;
if(callback!=null){
callback(arg1,arg2,arg3);
}else{
throwCreateBroadcastSignatureException(eventType);
}
}
}
#endregion
}
//This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level.
publicsealedclassMessengerHelper:MonoBehaviour{
voidAwake()
{
DontDestroyOnLoad(gameObject);
}
//Clean up eventTable every time a new level loads.
publicvoidOnDisable(){
Messenger.Cleanup();
}
}