/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Text; using System.Threading; using OpenMetaverse.Packets; using OpenMetaverse.Messages.Linden; using OpenMetaverse.Interfaces; namespace OpenMetaverse { /// /// Registers, unregisters, and fires events generated by incoming packets /// public class PacketEventDictionary { private sealed class PacketCallback { public EventHandler Callback; public bool IsAsync; public PacketCallback(EventHandler callback, bool isAsync) { Callback = callback; IsAsync = isAsync; } } /// /// Object that is passed to worker threads in the ThreadPool for /// firing packet callbacks /// private struct PacketCallbackWrapper { /// Callback to fire for this packet public EventHandler Callback; /// Reference to the simulator that this packet came from public Simulator Simulator; /// The packet that needs to be processed public Packet Packet; } /// Reference to the GridClient object public GridClient Client; private Dictionary _EventTable = new Dictionary(); /// /// Default constructor /// /// public PacketEventDictionary(GridClient client) { Client = client; } /// /// Register an event handler /// /// Use PacketType.Default to fire this event on every /// incoming packet /// Packet type to register the handler for /// Callback to be fired /// True if this callback should be ran /// asynchronously, false to run it synchronous public void RegisterEvent(PacketType packetType, EventHandler eventHandler, bool isAsync) { lock (_EventTable) { PacketCallback callback; if (_EventTable.TryGetValue(packetType, out callback)) { callback.Callback += eventHandler; callback.IsAsync = callback.IsAsync || isAsync; } else { callback = new PacketCallback(eventHandler, isAsync); _EventTable[packetType] = callback; } } } /// /// Unregister an event handler /// /// Packet type to unregister the handler for /// Callback to be unregistered public void UnregisterEvent(PacketType packetType, EventHandler eventHandler) { lock (_EventTable) { PacketCallback callback; if (_EventTable.TryGetValue(packetType, out callback)) { callback.Callback -= eventHandler; if (callback.Callback == null || callback.Callback.GetInvocationList().Length == 0) _EventTable.Remove(packetType); } } } /// /// Fire the events registered for this packet type /// /// Incoming packet type /// Incoming packet /// Simulator this packet was received from internal void RaiseEvent(PacketType packetType, Packet packet, Simulator simulator) { PacketCallback callback; // Default handler first, if one exists if (_EventTable.TryGetValue(PacketType.Default, out callback) && callback.Callback != null) { if (callback.IsAsync) { PacketCallbackWrapper wrapper; wrapper.Callback = callback.Callback; wrapper.Packet = packet; wrapper.Simulator = simulator; WorkPool.QueueUserWorkItem(ThreadPoolDelegate, wrapper); } else { try { callback.Callback(this, new PacketReceivedEventArgs(packet, simulator)); } catch (Exception ex) { Logger.Log("Default packet event handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } } } if (_EventTable.TryGetValue(packetType, out callback) && callback.Callback != null) { if (callback.IsAsync) { PacketCallbackWrapper wrapper; wrapper.Callback = callback.Callback; wrapper.Packet = packet; wrapper.Simulator = simulator; WorkPool.QueueUserWorkItem(ThreadPoolDelegate, wrapper); } else { try { callback.Callback(this, new PacketReceivedEventArgs(packet, simulator)); } catch (Exception ex) { Logger.Log("Packet event handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } } return; } if (packetType != PacketType.Default && packetType != PacketType.PacketAck) { Logger.DebugLog("No handler registered for packet event " + packetType, Client); } } private void ThreadPoolDelegate(Object state) { PacketCallbackWrapper wrapper = (PacketCallbackWrapper)state; try { wrapper.Callback(this, new PacketReceivedEventArgs(wrapper.Packet, wrapper.Simulator)); } catch (Exception ex) { Logger.Log("Async Packet Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } } } /// /// Registers, unregisters, and fires events generated by the Capabilities /// event queue /// public class CapsEventDictionary { /// /// Object that is passed to worker threads in the ThreadPool for /// firing CAPS callbacks /// private struct CapsCallbackWrapper { /// Callback to fire for this packet public Caps.EventQueueCallback Callback; /// Name of the CAPS event public string CapsEvent; /// Strongly typed decoded data public IMessage Message; /// Reference to the simulator that generated this event public Simulator Simulator; } /// Reference to the GridClient object public GridClient Client; private Dictionary _EventTable = new Dictionary(); private WaitCallback _ThreadPoolCallback; /// /// Default constructor /// /// Reference to the GridClient object public CapsEventDictionary(GridClient client) { Client = client; _ThreadPoolCallback = new WaitCallback(ThreadPoolDelegate); } /// /// Register an new event handler for a capabilities event sent via the EventQueue /// /// Use String.Empty to fire this event on every CAPS event /// Capability event name to register the /// handler for /// Callback to fire public void RegisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler) { // TODO: Should we add support for synchronous CAPS handlers? lock (_EventTable) { if (_EventTable.ContainsKey(capsEvent)) _EventTable[capsEvent] += eventHandler; else _EventTable[capsEvent] = eventHandler; } } /// /// Unregister a previously registered capabilities handler /// /// Capability event name unregister the /// handler for /// Callback to unregister public void UnregisterEvent(string capsEvent, Caps.EventQueueCallback eventHandler) { lock (_EventTable) { if (_EventTable.ContainsKey(capsEvent) && _EventTable[capsEvent] != null) _EventTable[capsEvent] -= eventHandler; } } /// /// Fire the events registered for this event type synchronously /// /// Capability name /// Decoded event body /// Reference to the simulator that /// generated this event internal void RaiseEvent(string capsEvent, IMessage message, Simulator simulator) { bool specialHandler = false; Caps.EventQueueCallback callback; // Default handler first, if one exists if (_EventTable.TryGetValue(capsEvent, out callback)) { if (callback != null) { try { callback(capsEvent, message, simulator); } catch (Exception ex) { Logger.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } } } // Explicit handler next if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null) { try { callback(capsEvent, message, simulator); } catch (Exception ex) { Logger.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } specialHandler = true; } if (!specialHandler) Logger.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning, Client); } /// /// Fire the events registered for this event type asynchronously /// /// Capability name /// Decoded event body /// Reference to the simulator that /// generated this event internal void BeginRaiseEvent(string capsEvent, IMessage message, Simulator simulator) { bool specialHandler = false; Caps.EventQueueCallback callback; // Default handler first, if one exists if (_EventTable.TryGetValue(String.Empty, out callback)) { if (callback != null) { callback(capsEvent, message, simulator); // CapsCallbackWrapper wrapper; // wrapper.Callback = callback; // wrapper.CapsEvent = capsEvent; // wrapper.Message = message; // wrapper.Simulator = simulator; // WorkPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper); } } // Explicit handler next if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null) { callback(capsEvent, message, simulator); // CapsCallbackWrapper wrapper; // wrapper.Callback = callback; // wrapper.CapsEvent = capsEvent; // wrapper.Message = message; // wrapper.Simulator = simulator; // WorkPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper); specialHandler = true; } if (!specialHandler) Logger.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning, Client); } private void ThreadPoolDelegate(Object state) { CapsCallbackWrapper wrapper = (CapsCallbackWrapper)state; try { wrapper.Callback(wrapper.CapsEvent, wrapper.Message, wrapper.Simulator); } catch (Exception ex) { Logger.Log("Async CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error, Client); } } } }