You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
14 KiB
C#

// 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;
/// <summary>
/// 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.
/// </summary>
/// <example>
/// Example of a simple publish and subscribe class, not using any id filtering.
///
/// Sender.cs
/// <code>
///
/// public enum SenderEvent {
/// Test
/// }
///
/// public class Sender {
/// public void Send() {
/// EventRouter.Publish(SenderEvent.Test, "Hello World");
/// }
/// }
/// </code>
///
/// Receiver.cs
/// <code>
///
/// 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<string>(0));
/// }
/// else {
/// Console.WriteLine("Received event: " + evt.Type + " with no data");
/// }
/// }
/// }
/// </code>
/// </example>
///
/// <example>
/// Example of a publish and subscribe using id's to filter specific messages.
///
/// Sender.cs
/// <code>
///
/// public enum SenderEvent {
/// Test
/// }
///
/// public class Sender {
/// public void SendA() {
/// EventRouter.Publish("A", SenderEvent.Test);
/// }
///
/// public void SendB() {
/// EventRouter.Publish("B", SenderEvent.Test);
/// }
/// }
/// </code>
///
/// Receiver.cs
/// <code>
///
/// 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'");
/// }
/// }
/// </code>
/// </example>
public class EventRouter {
/// <summary>
/// Event data class, passed in to the subscriber whenever an event occours.
/// </summary>
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<T>(int index) {
return (T)Data[index];
}
}
public delegate void Handler(Event e);
public static bool LogEvents = false;
static Dictionary<string, Dictionary<string, Handler>> handlers = new Dictionary<string, Dictionary<string, Handler>>();
static Dictionary<object, Dictionary<string, Handler>> owners = new Dictionary<object, Dictionary<string, Handler>>(); // For unsubscribing via object reference
static Queue<Event> queuedEvents = new Queue<Event>();
/// <summary>
/// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs.
/// </summary>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='h'>
/// The delegate to be called when the even occurs.
/// </param>
public static void Subscribe(Enum evt, Handler h, object owner = null) {
Subscribe("", EnumValueToString(evt), h, owner);
}
/// <summary>
/// Subscribe to the event specified by evt filtered by the specified id. Pass in a delegate to be called back when the even occurs.
/// </summary>
/// <param name='id'>
/// The id of the event
/// </param>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='h'>
/// The delegate to be called when the even occurs.
/// </param>
public static void Subscribe(string id, Enum evt, Handler h, object owner = null) {
Subscribe(id, EnumValueToString(evt), h, owner);
}
/// <summary>
/// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs.
/// </summary>
/// <param name='id'>
/// The string representing the id.
/// </param>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='h'>
/// The delegate to be called when the even occurs.
/// </param>
public static void Subscribe(string id, string evt, Handler h, object owner = null) {
if (!handlers.ContainsKey(evt)) {
handlers.Add(evt, new Dictionary<string, Handler>());
}
if (!handlers[evt].ContainsKey(id)) {
handlers[evt].Add(id, null);
}
if (owner != null) {
if (!owners.ContainsKey(owner)) {
owners.Add(owner, new Dictionary<string, Handler>());
}
owners[owner].Add(evt, h);
}
handlers[evt][id] += h;
}
/// <summary>
/// Subscribe to the event specified by evt. Pass in a delegate to be called back when the even occurs.
/// </summary>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='h'>
/// The delegate to be called when the even occurs.
/// </param>
public static void Subscribe(string evt, Handler h, object owner = null) {
Subscribe("", evt, h, owner);
}
/// <summary>
/// Unsubscribe the specified delegate from the event.
/// </summary>
/// <param name='id'>
/// The id of the event
/// </param>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='h'>
/// The delegate to be removed from the event handlers.
/// </param>
public static void Unsubscribe(string id, Enum evt, Handler h) {
Unsubscribe(id, EnumValueToString(evt), h);
}
/// <summary>
/// Unsubscribe the specified delegate from the event.
/// </summary>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='h'>
/// The delegate to be removed from the event handlers.
/// </param>
public static void Unsubscribe(Enum evt, Handler h) {
Unsubscribe(EnumValueToString(evt), h);
}
/// <summary>
/// Unsubscribe the specified delegate from the event.
/// </summary>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='h'>
/// The delegate to be removed from the event handlers.
/// </param>
public static void Unsubscribe(string evt, Handler h) {
Unsubscribe("", evt, h);
}
/// <summary>
/// Unsubscribe the specified delegate from the event.
/// </summary>
/// <param name='id'>
/// The string representing the id.
/// </param>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='h'>
/// The delegate to be removed from the event handlers.
/// </param>
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);
}
}
/// <summary>
/// Unsubscribe to all events that were subscribed to by a specific owner object.
/// </summary>
/// <param name="owner">The object that subscribed to the object.</param>
public static void Unsubscribe(object owner) {
if (owners.ContainsKey(owner)) {
foreach (var kv in owners[owner]) {
Unsubscribe(kv.Key, kv.Value);
}
owners.Remove(owner);
}
}
/// <summary>
/// Publish the specified event with extra data.
/// </summary>
/// <param name='id'>
/// The string representing the object.
/// </para>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='data'>
/// An arbitrary params array of objects to be interpreted by the receiver of the event.
/// </param>
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);
}
}
/// <summary>
/// Publish the specified event with extra data.
/// </summary>
/// <param name='evt'>
/// The string representing the event.
/// </param>
/// <param name='data'>
/// An arbitrary params array of objects to be interpreted by the receiver of the event.
/// </param>
public static void Publish(string evt, params object[] data) {
Publish("", evt, data);
}
/// <summary>
/// Publish the specified event with extra data.
/// </summary>
/// <param name='id'>
/// The string representing the object.
/// </para>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='data'>
/// An arbitrary params array of objects to be interpreted by the receiver of the event.
/// </param>
public static void Publish(string id, Enum evt, params object[] data) {
Publish(id, EnumValueToString(evt), data);
}
/// <summary>
/// Publish the specified event with extra data.
/// </summary>
/// <param name='evt'>
/// The event enumeration value.
/// </param>
/// <param name='data'>
/// An arbitrary params array of objects to be interpreted by the receiver of the event.
/// </param>
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);
}
/// <summary>
/// Clear all event subscribers, used primarily when switching or resetting a level.
/// </summary>
public static void Clear() {
handlers.Clear();
}
static void ProcessEvent(string id, string evt, params object[] data) {
Dictionary<string, Handler> 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);
}
}