// Copyright (c) 2009-2012 David Koontz, Dan Peschman, and Logan Barnett // Please direct any bugs/comments/suggestions to david@koontzfamily.org // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; using System.Collections.Generic; /// /// Class for broadcasting messages to subscribers. Clients of the EventRouter register /// themselves by calling Subscribe and passing in the even they are interested in along with /// a delegate to be called back when the event is received. Events are published via the /// Publish method which allows arbitrary data to be passed along with the event to be interpreted /// by the event receiver. Events do not have to be pre-defined. /// /// Publish and Subscribe methods also have an optional id parameter which will filter the events /// sent to just those that match the provided id. Subscribing to an event with no id will result /// in receiving all events of the specified type. /// /// /// Example of a simple publish and subscribe class, not using any id filtering. /// /// Sender.cs /// /// /// public enum SenderEvent { /// Test /// } /// /// public class Sender { /// public void Send() { /// EventRouter.Publish(SenderEvent.Test, "Hello World"); /// } /// } /// /// /// Receiver.cs /// /// /// public class Receiver { /// public Receiver() { /// EventRouter.Subscribe(SenderEvent.Test, OnSenderEvent); /// } /// /// void OnSenderEvent(EventRouter.Event evt) { /// if(evt.HasData) { /// Console.WriteLine("Received event: " + evt.Type + " with data: " + evt.GetData(0)); /// } /// else { /// Console.WriteLine("Received event: " + evt.Type + " with no data"); /// } /// } /// } /// /// /// /// /// Example of a publish and subscribe using id's to filter specific messages. /// /// Sender.cs /// /// /// public enum SenderEvent { /// Test /// } /// /// public class Sender { /// public void SendA() { /// EventRouter.Publish("A", SenderEvent.Test); /// } /// /// public void SendB() { /// EventRouter.Publish("B", SenderEvent.Test); /// } /// } /// /// /// Receiver.cs /// /// /// public class Receiver { /// public Receiver() { /// EventRouter.Subscribe("A", SenderEvent.Test, OnSenderEventA); /// EventRouter.Subscribe("B", SenderEvent.Test, OnSenderEventB); /// } /// /// void OnSenderEventA(EventRouter.Event evt) { /// Console.WriteLine("Received SenderEvent.Test with id 'A'"); /// } /// /// void OnSenderEventB(EventRouter.Event evt) { /// Console.WriteLine("Received SenderEvent.Test with id 'B'"); /// } /// } /// /// public class EventRouter { /// /// Event data class, passed in to the subscriber whenever an event occours. /// public class Event { public string Type; public string Id; public object[] Data; public bool HasData { get { return Data != null && Data.Length > 0; } } public T GetData(int index) { return (T)Data[index]; } } public delegate void Handler(Event e); public static bool LogEvents = false; static Dictionary> handlers = new Dictionary>(); static Dictionary> owners = new Dictionary>(); // For unsubscribing via object reference static Queue queuedEvents = new Queue(); /// /// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs. /// /// /// The event enumeration value. /// /// /// The delegate to be called when the even occurs. /// public static void Subscribe(Enum evt, Handler h, object owner = null) { Subscribe("", EnumValueToString(evt), h, owner); } /// /// Subscribe to the event specified by evt filtered by the specified id. Pass in a delegate to be called back when the even occurs. /// /// /// The id of the event /// /// /// The event enumeration value. /// /// /// The delegate to be called when the even occurs. /// public static void Subscribe(string id, Enum evt, Handler h, object owner = null) { Subscribe(id, EnumValueToString(evt), h, owner); } /// /// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs. /// /// /// The string representing the id. /// /// /// The string representing the event. /// /// /// The delegate to be called when the even occurs. /// public static void Subscribe(string id, string evt, Handler h, object owner = null) { if (!handlers.ContainsKey(evt)) { handlers.Add(evt, new Dictionary()); } if (!handlers[evt].ContainsKey(id)) { handlers[evt].Add(id, null); } if (owner != null) { if (!owners.ContainsKey(owner)) { owners.Add(owner, new Dictionary()); } owners[owner].Add(evt, h); } handlers[evt][id] += h; } /// /// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs. /// /// /// The string representing the event. /// /// /// The delegate to be called when the even occurs. /// public static void Subscribe(string evt, Handler h, object owner = null) { Subscribe("", evt, h, owner); } /// /// Unsubscribe the specified delegate from the event. /// /// /// The id of the event /// /// /// The event enumeration value. /// /// /// The delegate to be removed from the event handlers. /// public static void Unsubscribe(string id, Enum evt, Handler h) { Unsubscribe(id, EnumValueToString(evt), h); } /// /// Unsubscribe the specified delegate from the event. /// /// /// The event enumeration value. /// /// /// The delegate to be removed from the event handlers. /// public static void Unsubscribe(Enum evt, Handler h) { Unsubscribe(EnumValueToString(evt), h); } /// /// Unsubscribe the specified delegate from the event. /// /// /// The string representing the event. /// /// /// The delegate to be removed from the event handlers. /// public static void Unsubscribe(string evt, Handler h) { Unsubscribe("", evt, h); } /// /// Unsubscribe the specified delegate from the event. /// /// /// The string representing the id. /// /// /// The string representing the event. /// /// /// The delegate to be removed from the event handlers. /// public static void Unsubscribe(string id, string evt, Handler h) { if (handlers.ContainsKey(evt) && handlers[evt].ContainsKey(id)) { handlers[evt][id] -= h; if (handlers[evt][id] == null) handlers.Remove(evt); } } /// /// Unsubscribe to all events that were subscribed to by a specific owner object. /// /// The object that subscribed to the object. public static void Unsubscribe(object owner) { if (owners.ContainsKey(owner)) { foreach (var kv in owners[owner]) { Unsubscribe(kv.Key, kv.Value); } owners.Remove(owner); } } /// /// Publish the specified event with extra data. /// /// /// The string representing the object. /// /// /// The string representing the event. /// /// /// An arbitrary params array of objects to be interpreted by the receiver of the event. /// public static void Publish(string id, string evt, params object[] data) { //if (LogEvents) Debug.Log(string.Format("Event Published: '{0}' with id: '{1}' and {2} parameters", evt, id, data.Length)); ProcessEvent(id, evt, data); while (queuedEvents.Count > 0) { var queuedEvent = queuedEvents.Dequeue(); ProcessEvent(queuedEvent.Id, queuedEvent.Type, queuedEvent.Data); } } /// /// Publish the specified event with extra data. /// /// /// The string representing the event. /// /// /// An arbitrary params array of objects to be interpreted by the receiver of the event. /// public static void Publish(string evt, params object[] data) { Publish("", evt, data); } /// /// Publish the specified event with extra data. /// /// /// The string representing the object. /// /// /// The event enumeration value. /// /// /// An arbitrary params array of objects to be interpreted by the receiver of the event. /// public static void Publish(string id, Enum evt, params object[] data) { Publish(id, EnumValueToString(evt), data); } /// /// Publish the specified event with extra data. /// /// /// The event enumeration value. /// /// /// An arbitrary params array of objects to be interpreted by the receiver of the event. /// public static void Publish(Enum evt, params object[] data) { Publish(EnumValueToString(evt), data); } public static void Queue(string id, string evt, params object[] data) { var e = new Event { Type = evt, Id = id }; if (data != null && data.Length > 0) e.Data = data; queuedEvents.Enqueue(e); } public static void Queue(string evt, params object[] data) { Queue(string.Empty, evt, data); } public static void Queue(string id, Enum evt, params object[] data) { Queue(id, EnumValueToString(evt), data); } public static void Queue(Enum evt, params object[] data) { Queue(string.Empty, EnumValueToString(evt), data); } /// /// Clear all event subscribers, used primarily when switching or resetting a level. /// public static void Clear() { handlers.Clear(); } static void ProcessEvent(string id, string evt, params object[] data) { Dictionary handlersForType; if (handlers.TryGetValue(evt, out handlersForType)) { Handler handler; if (handlersForType.TryGetValue(id, out handler)) { var e = new Event { Type = evt, Id = id }; if (data != null && data.Length > 0) e.Data = data; handler(e); } // invoke handlers that don't care about a particular id if (id != string.Empty && handlersForType.TryGetValue(string.Empty, out handler)) { var e = new Event { Type = evt, Id = id }; if (data != null && data.Length > 0) e.Data = data; handler(e); } } } static string EnumValueToString(Enum value) { return string.Format("{0}.{1}", value.GetType(), value); } }