using System; using System.Collections.Generic; using System.Text; using System.Threading; using libsecondlife.Packets; namespace libsecondlife { /// /// Registers, unregisters, and fires events generated by incoming packets /// public class PacketEventDictionary { /// /// Object that is passed to worker threads in the ThreadPool for /// firing packet callbacks /// private struct PacketCallbackWrapper { /// Callback to fire for this packet public NetworkManager.PacketCallback 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 SecondLife client public SecondLife Client; private Dictionary _EventTable = new Dictionary(); private WaitCallback _ThreadPoolCallback; /// /// Default constructor /// /// public PacketEventDictionary(SecondLife client) { Client = client; _ThreadPoolCallback = new WaitCallback(ThreadPoolDelegate); } /// /// 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 public void RegisterEvent(PacketType packetType, NetworkManager.PacketCallback eventHandler) { lock (_EventTable) { if (_EventTable.ContainsKey(packetType)) _EventTable[packetType] += eventHandler; else _EventTable[packetType] = eventHandler; } } /// /// Unregister an event handler /// /// Packet type to unregister the handler for /// Callback to be unregistered public void UnregisterEvent(PacketType packetType, NetworkManager.PacketCallback eventHandler) { lock (_EventTable) { if (_EventTable.ContainsKey(packetType) && _EventTable[packetType] != null) _EventTable[packetType] -= eventHandler; } } /// /// Fire the events registered for this packet type synchronously /// /// Incoming packet type /// Incoming packet /// Simulator this packet was received from internal void RaiseEvent(PacketType packetType, Packet packet, Simulator simulator) { NetworkManager.PacketCallback callback; if (_EventTable.TryGetValue(packetType, out callback)) { try { callback(packet, simulator); } catch (Exception ex) { Client.Log("Packet Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); } } else if (packetType != PacketType.Default && packetType != PacketType.PacketAck) { Client.Log("No handler registered for packet event " + packetType, Helpers.LogLevel.Debug); } } /// /// Fire the events registered for this packet type asynchronously /// /// Incoming packet type /// Incoming packet /// Simulator this packet was received from internal void BeginRaiseEvent(PacketType packetType, Packet packet, Simulator simulator) { NetworkManager.PacketCallback callback; if (_EventTable.TryGetValue(packetType, out callback)) { if (callback != null) { PacketCallbackWrapper wrapper; wrapper.Callback = callback; wrapper.Packet = packet; wrapper.Simulator = simulator; ThreadPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper); return; } } if (packetType != PacketType.Default && packetType != PacketType.PacketAck) { Client.Log("No handler registered for packet event " + packetType, Helpers.LogLevel.Debug); } } private void ThreadPoolDelegate(Object state) { PacketCallbackWrapper wrapper = (PacketCallbackWrapper)state; try { wrapper.Callback(wrapper.Packet, wrapper.Simulator); } catch (Exception ex) { Client.Log("Async Packet Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); } } } /// /// 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; /// Decoded body of the CAPS event public StructuredData.LLSD Body; /// Reference to the simulator that generated this event public Simulator Simulator; } /// Reference to the SecondLife client public SecondLife Client; private Dictionary _EventTable = new Dictionary(); private WaitCallback _ThreadPoolCallback; /// /// Default constructor /// /// Reference to the SecondLife client public CapsEventDictionary(SecondLife client) { Client = client; _ThreadPoolCallback = new WaitCallback(ThreadPoolDelegate); } /// /// Register an event handler /// /// 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) { lock (_EventTable) { if (_EventTable.ContainsKey(capsEvent)) _EventTable[capsEvent] += eventHandler; else _EventTable[capsEvent] = eventHandler; } } /// /// /// /// 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, StructuredData.LLSD body, 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, body, simulator); } catch (Exception ex) { Client.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); } } } // Generic parser next if (body.Type == StructuredData.LLSDType.Map) { StructuredData.LLSDMap map = (StructuredData.LLSDMap)body; Packet packet = Packet.BuildPacket(capsEvent, map); if (packet != null) { NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = simulator; incomingPacket.Packet = packet; Client.DebugLog("Serializing " + packet.Type.ToString() + " capability with generic handler"); Client.Network.PacketInbox.Enqueue(incomingPacket); specialHandler = true; } } // Explicit handler next if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null) { try { callback(capsEvent, body, simulator); } catch (Exception ex) { Client.Log("CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); } specialHandler = true; } if (!specialHandler) Client.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning); } /// /// 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, StructuredData.LLSD body, Simulator simulator) { bool specialHandler = false; Caps.EventQueueCallback callback; // Default handler first, if one exists if (_EventTable.TryGetValue(String.Empty, out callback)) { if (callback != null) { CapsCallbackWrapper wrapper; wrapper.Callback = callback; wrapper.CapsEvent = capsEvent; wrapper.Body = body; wrapper.Simulator = simulator; ThreadPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper); } } // Generic parser next, don't generic parse events we've manually registered for if (body.Type == StructuredData.LLSDType.Map && !_EventTable.ContainsKey(capsEvent)) { StructuredData.LLSDMap map = (StructuredData.LLSDMap)body; Packet packet = Packet.BuildPacket(capsEvent, map); if (packet != null) { NetworkManager.IncomingPacket incomingPacket; incomingPacket.Simulator = simulator; incomingPacket.Packet = packet; Client.DebugLog("Serializing " + packet.Type.ToString() + " capability with generic handler"); Client.Network.PacketInbox.Enqueue(incomingPacket); specialHandler = true; } } // Explicit handler next if (_EventTable.TryGetValue(capsEvent, out callback) && callback != null) { CapsCallbackWrapper wrapper; wrapper.Callback = callback; wrapper.CapsEvent = capsEvent; wrapper.Body = body; wrapper.Simulator = simulator; ThreadPool.QueueUserWorkItem(_ThreadPoolCallback, wrapper); specialHandler = true; } if (!specialHandler) Client.Log("Unhandled CAPS event " + capsEvent, Helpers.LogLevel.Warning); } private void ThreadPoolDelegate(Object state) { CapsCallbackWrapper wrapper = (CapsCallbackWrapper)state; try { wrapper.Callback(wrapper.CapsEvent, wrapper.Body, wrapper.Simulator); } catch (Exception ex) { Client.Log("Async CAPS Event Handler: " + ex.ToString(), Helpers.LogLevel.Error); } } } }