/************************************************************************************ Filename : OVRMessenger.cs Content : Base component OVR class Created : January 8, 2013 Authors : Peter Giokaris Copyright : Copyright 2014 Oculus VR, LLC. All Rights reserved. Use of this software is subject to the terms of the Oculus LLC license agreement provided at the time of installation or download, or which otherwise accompanies this software in either electronic or hard copy form. Messenger.cs from Unity3d: http://wiki.unity3d.com/index.php/Advanced_CSharp_Messenger Renamed to OVRMessenger * 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("prop collected", PropCollected); Messenger.Broadcast("prop collected", prop); 2. Messenger.AddListener("speed changed", SpeedChanged); Messenger.Broadcast("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 using System; using System.Collections.Generic; using UnityEngine; // Add more delegate types here and implement them the same way below.. public delegate void OVRCallback(); public delegate void OVRCallback(T arg1); public delegate void OVRCallback(T arg1, U arg2); public delegate void OVRCallback(T arg1, U arg2, V arg3); static internal class OVRMessenger { #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. static private MessengerHelper messengerHelper = ( new GameObject("MessengerHelper") ).AddComponent< MessengerHelper >(); #pragma warning restore 0414 static public Dictionary eventTable = new Dictionary(); //Message handlers that should never be removed, regardless of calling Cleanup static public List< string > permanentMessages = new List< string > (); #endregion #region Helper methods /// /// Marks a certain message as permanent. /// /// Event type. static public void MarkAsPermanent(string eventType) { #if LOG_ALL_MESSAGES Debug.Log("Messenger MarkAsPermanent \t\"" + eventType + "\""); #endif permanentMessages.Add( eventType ); } /// /// Cleanup this instance. /// static public void Cleanup() { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER Cleanup. Make sure that none of necessary listeners are removed."); #endif List< string > messagesToRemove = new List(); foreach (KeyValuePair pair in eventTable) { bool wasFound = false; foreach (string message in permanentMessages) { if (pair.Key == message) { wasFound = true; break; } } if (!wasFound) messagesToRemove.Add( pair.Key ); } foreach (string message in messagesToRemove) { eventTable.Remove( message ); } } /// /// Prints the event table. /// static public void PrintEventTable() { Debug.Log("\t\t\t=== MESSENGER PrintEventTable ==="); foreach (KeyValuePair pair in eventTable) { Debug.Log("\t\t\t" + pair.Key + "\t\t" + pair.Value); } Debug.Log("\n"); } #endregion #region Message logging and exception throwing /// /// Raises the listener adding event. /// /// Event type. /// Listener being added. static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) { #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 ); } Delegate d = eventTable[eventType]; if (d != null && d.GetType() != listenerBeingAdded.GetType()) { throw new ListenerException(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)); } } /// /// Raises the listener removing event. /// /// Event type. /// Listener being removed. static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) { #if LOG_ALL_MESSAGES Debug.Log("MESSENGER OnListenerRemoving \t\"" + eventType + "\"\t{" + listenerBeingRemoved.Target + " -> " + listenerBeingRemoved.Method + "}"); #endif if (eventTable.ContainsKey(eventType)) { Delegate d = eventTable[eventType]; if (d == null) { throw new ListenerException(string.Format("Attempting to remove listener with for event type \"{0}\" but current listener is null.", eventType)); } else if (d.GetType() != listenerBeingRemoved.GetType()) { throw new ListenerException(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 { throw new ListenerException(string.Format("Attempting to remove listener for type \"{0}\" but Messenger doesn't know about this event type.", eventType)); } } /// /// Raises the listener removed event. /// /// Event type. static public void OnListenerRemoved(string eventType) { if (eventTable[eventType] == null) { eventTable.Remove(eventType); } } /// /// Raises the broadcasting event. /// /// Event type. static public void OnBroadcasting(string eventType) { #if REQUIRE_LISTENER if (!eventTable.ContainsKey(eventType)) { throw new BroadcastException(string.Format("Broadcasting message \"{0}\" but no listener found. Try marking the message with Messenger.MarkAsPermanent.", eventType)); } #endif } /// /// Creates the broadcast signature exception. /// /// The broadcast signature exception. /// Event type. static public BroadcastException CreateBroadcastSignatureException(string eventType) { return new BroadcastException(string.Format("Broadcasting message \"{0}\" but listeners have a different signature than the broadcaster.", eventType)); } public class BroadcastException : Exception { public BroadcastException(string msg) : base(msg) { } } public class ListenerException : Exception { public ListenerException(string msg) : base(msg) { } } #endregion #region AddListener /// /// No parameters. /// /// Event type. /// Handler. static public void AddListener(string eventType, OVRCallback handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] + handler; } /// /// Single parameter. /// /// Event type. /// Handler. /// The 1st type parameter. static public void AddListener(string eventType, OVRCallback handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] + handler; } /// /// Two parameters. /// /// Event type. /// Handler. /// The 1st type parameter. /// The 2nd type parameter. static public void AddListener(string eventType, OVRCallback handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] + handler; } /// /// Three parameters /// Event type. /// Handler. /// The 1st type parameter. /// The 2nd type parameter. /// The 3rd type parameter. static public void AddListener(string eventType, OVRCallback handler) { OnListenerAdding(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] + handler; } #endregion #region RemoveListener /// /// No parameters /// /// Event type. /// Handler. static public void RemoveListener(string eventType, OVRCallback handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] - handler; OnListenerRemoved(eventType); } /// /// Single parameter /// /// Event type. /// Handler. /// The 1st type parameter. static public void RemoveListener(string eventType, OVRCallback handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] - handler; OnListenerRemoved(eventType); } /// /// Two parameters /// /// Event type. /// Handler. /// The 1st type parameter. /// The 2nd type parameter. static public void RemoveListener(string eventType, OVRCallback handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] - handler; OnListenerRemoved(eventType); } /// //Three parameters. /// /// Event type. /// Handler. /// The 1st type parameter. /// The 2nd type parameter. /// The 3rd type parameter. static public void RemoveListener(string eventType, OVRCallback handler) { OnListenerRemoving(eventType, handler); eventTable[eventType] = (OVRCallback)eventTable[eventType] - handler; OnListenerRemoved(eventType); } #endregion #region Broadcast /// /// Broadcast the specified eventType. /// /// Event type. static public void Broadcast(string eventType) { #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); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { OVRCallback callback = d as OVRCallback; if (callback != null) { callback(); } else { throw CreateBroadcastSignatureException(eventType); } } } /// /// Broadcast the specified eventType and arg1. /// /// Event type. /// Arg1. /// The 1st type parameter. static public void Broadcast(string eventType, T arg1) { #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); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { OVRCallback callback = d as OVRCallback; if (callback != null) { callback(arg1); } else { throw CreateBroadcastSignatureException(eventType); } } } /// /// Broadcast the specified eventType, arg1 and arg2. /// /// Event type. /// Arg1. /// Arg2. /// The 1st type parameter. /// The 2nd type parameter. static public void Broadcast(string eventType, T arg1, U arg2) { #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); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { OVRCallback callback = d as OVRCallback; if (callback != null) { callback(arg1, arg2); } else { throw CreateBroadcastSignatureException(eventType); } } } /// /// Broadcast the specified eventType, arg1, arg2 and arg3. /// /// Event type. /// Arg1. /// Arg2. /// Arg3. /// The 1st type parameter. /// The 2nd type parameter. /// The 3rd type parameter. static public void Broadcast(string eventType, T arg1, U arg2, V arg3) { #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); Delegate d; if (eventTable.TryGetValue(eventType, out d)) { OVRCallback callback = d as OVRCallback; if (callback != null) { callback(arg1, arg2, arg3); } else { throw CreateBroadcastSignatureException(eventType); } } } #endregion } /// /// Messenger helper. /// This manager will ensure that the messenger's eventTable will be cleaned up upon loading of a new level. /// public sealed class MessengerHelper : MonoBehaviour { /// /// Awake this instance. /// void Awake () { DontDestroyOnLoad(gameObject); } /// /// Raises the disable event. /// public void OnDisable() { OVRMessenger.Cleanup(); } }