From 29aa41daa004531cc41649c1818e4e432600cc32 Mon Sep 17 00:00:00 2001 From: Sean Dague Date: Fri, 5 Oct 2007 19:33:26 +0000 Subject: Code from Illumious Beltran (IBM) implementing more LSL The functions implemented are: llListen llListenControl llListenRemove llOpenRemoteDataChannel llCloseRemoteDataChannel llRemoteDataReply The events implemented are: listen remote_data --- .../Region/Environment/Modules/WorldCommModule.cs | 481 +++++++++++++++++++++ 1 file changed, 481 insertions(+) create mode 100644 OpenSim/Region/Environment/Modules/WorldCommModule.cs (limited to 'OpenSim/Region/Environment/Modules/WorldCommModule.cs') diff --git a/OpenSim/Region/Environment/Modules/WorldCommModule.cs b/OpenSim/Region/Environment/Modules/WorldCommModule.cs new file mode 100644 index 0000000..c2ec699 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/WorldCommModule.cs @@ -0,0 +1,481 @@ +using System; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using libsecondlife; +using OpenSim.Framework.Interfaces; +using OpenSim.Framework.Utilities; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Framework.Servers; +using Nwc.XmlRpc; +using System.Collections; +using System.Collections.Generic; + +/***************************************************** + * + * WorldCommModule + * + * + * Holding place for world comms - basically llListen + * function implementation. + * + * lLListen(integer channel, string name, key id, string msg) + * The name, id, and msg arguments specify the filtering + * criteria. You can pass the empty string + * (or NULL_KEY for id) for these to set a completely + * open filter; this causes the listen() event handler to be + * invoked for all chat on the channel. To listen only + * for chat spoken by a specific object or avatar, + * specify the name and/or id arguments. To listen + * only for a specific command, specify the + * (case-sensitive) msg argument. If msg is not empty, + * listener will only hear strings which are exactly equal + * to msg. You can also use all the arguments to establish + * the most restrictive filtering criteria. + * + * It might be useful for each listener to maintain a message + * digest, with a list of recent messages by UUID. This can + * be used to prevent in-world repeater loops. However, the + * linden functions do not have this capability, so for now + * thats the way it works. + * + * **************************************************/ +namespace OpenSim.Region.Environment.Modules +{ + public class WorldCommModule : IRegionModule, IWorldComm + { + private Scene m_scene; + private object CommListLock = new object(); + private string m_name = "WorldCommModule"; + private ListenerManager m_listenerManager; + private Queue m_pending; + + public WorldCommModule() + { + } + + public void Initialise(Scene scene) + { + + m_scene = scene; + m_scene.RegisterModuleInterface(this); + m_listenerManager = new ListenerManager(); + m_pending = new Queue(); + m_scene.EventManager.OnNewClient += NewClient; + + } + + public void PostInitialise() + { + } + + public void CloseDown() + { + } + + public string GetName() + { + return m_name; + } + + public bool IsSharedModule() + { + return false; + } + + public void NewClient(IClientAPI client) + { + client.OnChatFromViewer += DeliverClientMessage; + } + + private void DeliverClientMessage(byte[] message, byte type, int channel, LLVector3 fromPos, string fromName, + LLUUID fromAgentID) + { + ASCIIEncoding ae = new ASCIIEncoding(); + + DeliverMessage(fromAgentID.ToString(), type, channel, fromName, ae.GetString(message)); + + } + + public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) + { + return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); + } + + public void ListenControl(int handle, int active) + { + if ( active == 1 ) + m_listenerManager.Activate(handle); + else if ( active == 0 ) + m_listenerManager.Dectivate(handle); + + } + + public void ListenRemove(int handle) + { + m_listenerManager.Remove(handle); + } + + // This method scans nearby objects and determines if they are listeners, + // and if so if this message fits the filter. If it does, then + // enqueue the message for delivery to the objects listen event handler. + // Objects that do an llSay have their messages delivered here, and for + // nearby avatards, the SimChat function is used. + public void DeliverMessage(string sourceItemID, int type, int channel, string name, string msg) + { + + SceneObjectPart source = null; + ScenePresence avatar = null; + + source = m_scene.GetSceneObjectPart(new LLUUID(sourceItemID)); + if (source == null) + { + avatar = m_scene.GetScenePresence(new LLUUID(sourceItemID)); + } + if( (avatar != null) || (source != null) ) + { + // Loop through the objects in the scene + // If they are in proximity, then if they are + // listeners, if so add them to the pending queue + + foreach (LLUUID eb in m_scene.Entities.Keys) + { + EntityBase sPart; + + m_scene.Entities.TryGetValue(eb, out sPart); + + // Dont process if this message is from itself! + if (eb.ToString().Equals(sourceItemID) || + sPart.UUID.ToString().Equals(sourceItemID) ) + continue; + + double dis = 0; + + if (source != null) + dis = sPart.AbsolutePosition.GetDistanceTo(source.AbsolutePosition); + else + dis = sPart.AbsolutePosition.GetDistanceTo(avatar.AbsolutePosition); + + switch (type) + { + case 0: // Whisper + + if ((dis < 10) && (dis > -10)) + { + ListenerInfo isListener = m_listenerManager.IsListenerMatch( + sourceItemID, sPart.UUID, channel, name, msg + ); + if (isListener != null) + { + m_pending.Enqueue(isListener); + } + + } + break; + + case 1: // Say + + if ((dis < 30) && (dis > -30)) + { + ListenerInfo isListener = m_listenerManager.IsListenerMatch( + sourceItemID, sPart.UUID, channel, name, msg + ); + if (isListener != null) + { + m_pending.Enqueue(isListener); + } + + } + break; + + case 2: // Shout + if ((dis < 100) && (dis > -100)) + { + ListenerInfo isListener = m_listenerManager.IsListenerMatch( + sourceItemID, sPart.UUID, channel, name, msg + ); + if (isListener != null) + { + m_pending.Enqueue(isListener); + } + + } + break; + + case 0xff: // Broadcast + ListenerInfo isListen = m_listenerManager.IsListenerMatch(sourceItemID, eb, channel, name, msg); + if (isListen != null) + { + ListenerInfo isListener = m_listenerManager.IsListenerMatch( + sourceItemID, sPart.UUID, channel, name, msg + ); + if (isListener != null) + { + m_pending.Enqueue(isListener); + } + } + break; + } + }; + + } + + } + + public bool HasMessages() + { + return (m_pending.Count > 0); + } + + public ListenerInfo GetNextMessage() + { + + ListenerInfo li = null; + + lock (CommListLock) + { + li = m_pending.Dequeue(); + } + + return li; + + } + + } + + // hostID: the ID of the ScenePart + // itemID: the ID of the script host engine + // localID: local ID of host engine + public class ListenerManager + { + private Dictionary m_listeners; + private object ListenersLock = new object(); + private int m_MaxListeners = 100; + + public ListenerManager() + { + m_listeners = new Dictionary(); + } + + public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) + { + + if ( m_listeners.Count < m_MaxListeners ) + { + ListenerInfo isListener = IsListenerMatch(LLUUID.Zero.ToString(), itemID, channel, name, msg); + + if(isListener == null) + { + int newHandle = GetNewHandle(); + + if (newHandle > -1) + { + + ListenerInfo li = new ListenerInfo(localID, newHandle, itemID, hostID, channel, name, id, msg); + + lock (ListenersLock) + { + m_listeners.Add(newHandle, li); + } + + return newHandle; + } + + } + + } + + return -1; + + } + + public void Remove(int handle) + { + m_listeners.Remove(handle); + } + + private int GetNewHandle() + { + + for (int i = 0; i < int.MaxValue - 1; i++) + { + if (!m_listeners.ContainsKey(i)) + return i; + } + + return -1; + + } + + public bool IsListener(LLUUID hostID) + { + + foreach (ListenerInfo li in m_listeners.Values) + { + if (li.GetHostID().Equals(hostID)) + return true; + } + + return false; + + } + + public void Activate(int handle) + { + + ListenerInfo li; + + if( m_listeners.TryGetValue(handle, out li) ) + { + li.Activate(); + } + } + + public void Dectivate(int handle) + { + + ListenerInfo li; + + if( m_listeners.TryGetValue(handle, out li) ) + { + li.Deactivate(); + } + } + + // Theres probably a more clever and efficient way to + // do this, maybe with regex. + public ListenerInfo IsListenerMatch(string sourceItemID, LLUUID listenerKey, int channel, string name, string msg) + { + + bool isMatch = true; + + foreach (ListenerInfo li in m_listeners.Values) + { + if (li.GetHostID().Equals(listenerKey)) + { + if ( li.IsActive() ) + { + if ( channel == li.GetChannel() ) + { + if ( (li.GetID().ToString().Length > 0) && + (!li.GetID().Equals(LLUUID.Zero)) ) + { + if (!li.GetID().ToString().Equals(sourceItemID)) + { + isMatch = false; + } + } + if ( isMatch && (li.GetName().Length > 0) ) + { + if ( li.GetName().Equals(name) ) + { + isMatch = false; + } + } + if ( isMatch ) + { + return new ListenerInfo( + li.GetLocalID(), li.GetHandle(), li.GetItemID(), li.GetHostID(), + li.GetChannel(), name, li.GetID(), msg, new LLUUID(sourceItemID) + ); + } + } + } + } + } + return null; + } + + } + + public class ListenerInfo + { + + private LLUUID m_itemID; // ID of the host script engine + private LLUUID m_hostID; // ID of the host/scene part + private LLUUID m_sourceItemID; // ID of the scenePart or avatar source of the message + private int m_channel; // Channel + private int m_handle; // Assigned handle of this listener + private uint m_localID; // Local ID from script engine + private string m_name; // Object name to filter messages from + private LLUUID m_id; // ID to filter messages from + private string m_message; // The message + private bool m_active; // Listener is active or not + + public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message) + { + Initialise(localID, handle, ItemID, hostID, channel, name, id, message); + } + + public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message, LLUUID sourceItemID) + { + Initialise(localID, handle, ItemID, hostID, channel, name, id, message); + m_sourceItemID = sourceItemID; + } + + private void Initialise(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message) + { + m_handle = handle; + m_channel = channel; + m_itemID = ItemID; + m_hostID = hostID; + m_name = name; + m_id = id; + m_message = message; + m_active = true; + m_localID = localID; + } + public LLUUID GetItemID() + { + return m_itemID; + } + public LLUUID GetHostID() + { + return m_hostID; + } + public LLUUID GetSourceItemID() + { + return m_sourceItemID; + } + public int GetChannel() + { + return m_channel; + } + public uint GetLocalID() + { + return m_localID; + } + public int GetHandle() + { + return m_handle; + } + public string GetMessage() + { + return m_message; + } + public string GetName() + { + return m_name; + } + public bool IsActive() + { + return m_active; + } + public void Deactivate() + { + m_active = false; + } + public void Activate() + { + m_active = true; + } + public LLUUID GetID() + { + return m_id; + } + + } + +} -- cgit v1.1