* Applying patch #1121 - Fixes for llListen() (Thanks Middlelink!)
parent
cf7560d1aa
commit
dc21e8d5e2
|
@ -55,6 +55,8 @@ namespace OpenSim.Framework
|
|||
StartTyping = 4,
|
||||
StopTyping = 5,
|
||||
DebugChannel = 6,
|
||||
Region = 7,
|
||||
Owner = 8,
|
||||
Broadcast = 0xFF
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,12 @@ namespace OpenSim.Region.Environment.Interfaces
|
|||
{
|
||||
public interface IWorldComm
|
||||
{
|
||||
int Listen(uint LocalID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg);
|
||||
void DeliverMessage(string sourceItemID, ChatTypeEnum type, int channel, string name, string msg);
|
||||
int Listen(uint LocalID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg);
|
||||
void DeliverMessage(ChatTypeEnum type, int channel, string name, LLUUID id, string msg);
|
||||
bool HasMessages();
|
||||
ListenerInfo GetNextMessage();
|
||||
void ListenControl(int handle, int active);
|
||||
void ListenRemove(int handle);
|
||||
void ListenControl(LLUUID itemID, int handle, int active);
|
||||
void ListenRemove(LLUUID itemID, int handle);
|
||||
void DeleteListener(LLUUID itemID);
|
||||
uint PeekNextMessageLocalID();
|
||||
LLUUID PeekNextMessageItemID();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using libsecondlife;
|
||||
using Nini.Config;
|
||||
using OpenSim.Framework;
|
||||
|
@ -60,6 +61,21 @@ using OpenSim.Region.Environment.Scenes;
|
|||
* 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.
|
||||
* Instead it blocks messages originating from the same prim.
|
||||
* (not Object!)
|
||||
*
|
||||
* For LSL compliance, note the following:
|
||||
* (Tested again 1.21.1 on May 2, 2008)
|
||||
* 1. 'id' has to be parsed into a LLUUID. None-UUID keys are
|
||||
* to be replaced by the ZeroID key. (Well, TryParse does
|
||||
* that for us.
|
||||
* 2. Setting up an listen event from the same script, with the
|
||||
* same filter settings (including step 1), returns the same
|
||||
* handle as the original filter.
|
||||
* 3. (TODO) handles should be script-local. Starting from 1.
|
||||
* Might be actually easier to map the global handle into
|
||||
* script-local handle in the ScriptEngine. Not sure if its
|
||||
* worth the effort tho.
|
||||
*
|
||||
* **************************************************/
|
||||
|
||||
|
@ -67,25 +83,39 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
{
|
||||
public class WorldCommModule : IRegionModule, IWorldComm
|
||||
{
|
||||
private object CommListLock = new object();
|
||||
private object ListLock = new object();
|
||||
private ListenerManager m_listenerManager;
|
||||
private string m_name = "WorldCommModule";
|
||||
private Queue m_pending;
|
||||
private Queue m_pendingQ;
|
||||
private Scene m_scene;
|
||||
|
||||
public WorldCommModule()
|
||||
{
|
||||
}
|
||||
private int m_whisperdistance = 10;
|
||||
private int m_saydistance = 30;
|
||||
private int m_shoutdistance = 100;
|
||||
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
// wrap this in a try block so that defaults will work if
|
||||
// the config file doesn't specify otherwise.
|
||||
int maxlisteners = 1000;
|
||||
int maxhandles = 64;
|
||||
try
|
||||
{
|
||||
m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance);
|
||||
m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance);
|
||||
m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance);
|
||||
maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners);
|
||||
maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
if (maxlisteners < 1) maxlisteners = int.MaxValue;
|
||||
if (maxhandles < 1) maxhandles = int.MaxValue;
|
||||
|
||||
m_scene = scene;
|
||||
m_scene.RegisterModuleInterface<IWorldComm>(this);
|
||||
m_listenerManager = new ListenerManager();
|
||||
m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
|
||||
m_scene.EventManager.OnNewClient += NewClient;
|
||||
m_pendingQ = new Queue();
|
||||
m_pending = Queue.Synchronized(m_pendingQ);
|
||||
|
@ -101,7 +131,7 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
|
||||
public string Name
|
||||
{
|
||||
get { return m_name; }
|
||||
get { return "WorldCommModule"; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
|
@ -113,170 +143,160 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
|
||||
#region IWorldComm Members
|
||||
|
||||
public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg)
|
||||
/// <summary>
|
||||
/// Create a listen event callback with the specified filters.
|
||||
/// The parameters localID,itemID are needed to uniquely identify
|
||||
/// the script during 'peek' time. Parameter hostID is needed to
|
||||
/// determine the position of the script.
|
||||
/// </summary>
|
||||
/// <param name="localID">localID of the script engine</param>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="hostID">UUID of the SceneObjectPart</param>
|
||||
/// <param name="channel">channel to listen on</param>
|
||||
/// <param name="name">name to filter on</param>
|
||||
/// <param name="id">key to filter on (user given, could be totally faked)</param>
|
||||
/// <param name="msg">msg to filter on</param>
|
||||
/// <returns>number of the scripts handle</returns>
|
||||
public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg)
|
||||
{
|
||||
return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
|
||||
}
|
||||
|
||||
public void ListenControl(int handle, int active)
|
||||
{
|
||||
if (m_listenerManager != null)
|
||||
/// <summary>
|
||||
/// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
|
||||
/// The handle used is returned from Listen()
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="handle">handle returned by Listen()</param>
|
||||
/// <param name="active">temp. activate or deactivate the Listen()</param>
|
||||
public void ListenControl(LLUUID itemID, int handle, int active)
|
||||
{
|
||||
if (active == 1)
|
||||
m_listenerManager.Activate(handle);
|
||||
m_listenerManager.Activate(itemID, handle);
|
||||
else if (active == 0)
|
||||
m_listenerManager.Dectivate(handle);
|
||||
}
|
||||
m_listenerManager.Dectivate(itemID, handle);
|
||||
}
|
||||
|
||||
public void ListenRemove(int handle)
|
||||
/// <summary>
|
||||
/// Removes the listen event callback with handle
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
/// <param name="handle">handle returned by Listen()</param>
|
||||
public void ListenRemove(LLUUID itemID, int handle)
|
||||
{
|
||||
if (m_listenerManager != null)
|
||||
{
|
||||
m_listenerManager.Remove(handle);
|
||||
}
|
||||
m_listenerManager.Remove(itemID, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all listen event callbacks for the given itemID
|
||||
/// (script engine)
|
||||
/// </summary>
|
||||
/// <param name="itemID">UUID of the script engine</param>
|
||||
public void DeleteListener(LLUUID itemID)
|
||||
{
|
||||
if (m_listenerManager != null)
|
||||
{
|
||||
m_listenerManager.DeleteListener(itemID);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 avatars, the SimChat function is used.
|
||||
public void DeliverMessage(string sourceItemID, ChatTypeEnum type, int channel, string name, string msg)
|
||||
/// <summary>
|
||||
/// This method scans over the objects which registered an interest in listen callbacks.
|
||||
/// For everyone it finds, it checks if it fits the given filter. If it does, then
|
||||
/// enqueue the message for delivery to the objects listen event handler.
|
||||
/// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
|
||||
/// Objects that do an llSay have their messages delivered here and for nearby avatars,
|
||||
/// the OnChatFromViewer event is used.
|
||||
/// </summary>
|
||||
/// <param name="type">type of delvery (whisper,say,shout or regionwide)</param>
|
||||
/// <param name="channel">channel to sent on</param>
|
||||
/// <param name="name">name of sender (object or avatar)</param>
|
||||
/// <param name="id">key of sender (object or avatar)</param>
|
||||
/// <param name="msg">msg to sent</param>
|
||||
public void DeliverMessage(ChatTypeEnum type, int channel, string name, LLUUID id, string msg)
|
||||
{
|
||||
SceneObjectPart source = null;
|
||||
ScenePresence avatar = null;
|
||||
LLVector3 position;
|
||||
|
||||
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 (ListenerInfo li in m_listenerManager.GetListeners())
|
||||
{
|
||||
EntityBase sPart;
|
||||
|
||||
m_scene.Entities.TryGetValue(li.GetHostID(), out sPart);
|
||||
|
||||
if (sPart != null)
|
||||
{
|
||||
double dis = 0;
|
||||
|
||||
source = m_scene.GetSceneObjectPart(id);
|
||||
if (source != null)
|
||||
dis = Util.GetDistanceTo(sPart.AbsolutePosition, source.AbsolutePosition);
|
||||
position = source.AbsolutePosition;
|
||||
else {
|
||||
avatar = m_scene.GetScenePresence(id);
|
||||
if (avatar != null)
|
||||
position = avatar.AbsolutePosition;
|
||||
else
|
||||
dis = Util.GetDistanceTo(sPart.AbsolutePosition, avatar.AbsolutePosition);
|
||||
// bail out early, given source could not be found
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine which listen event filters match the given set of arguments, this results
|
||||
// in a limited set of listeners, each belonging a host. If the host is in range, add them
|
||||
// to the pending queue.
|
||||
foreach (ListenerInfo li in m_listenerManager.GetListeners(LLUUID.Zero, channel, name, id, msg))
|
||||
{
|
||||
// Dont process if this message is from yourself!
|
||||
if (li.GetHostID().Equals(id))
|
||||
continue;
|
||||
|
||||
SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID());
|
||||
if (sPart == null)
|
||||
continue;
|
||||
|
||||
double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position);
|
||||
switch (type)
|
||||
{
|
||||
case ChatTypeEnum.Whisper:
|
||||
|
||||
if ((dis < 10) && (dis > -10))
|
||||
{
|
||||
if (li.GetChannel() == channel)
|
||||
{
|
||||
ListenerInfo isListener = m_listenerManager.IsListenerMatch(
|
||||
sourceItemID, sPart.UUID, channel, name, msg
|
||||
);
|
||||
if (isListener != null)
|
||||
if (dis < m_whisperdistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(isListener);
|
||||
}
|
||||
}
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Say:
|
||||
|
||||
if ((dis < 30) && (dis > -30))
|
||||
{
|
||||
if (li.GetChannel() == channel)
|
||||
{
|
||||
ListenerInfo isListener = m_listenerManager.IsListenerMatch(
|
||||
sourceItemID, sPart.UUID, channel, name, msg
|
||||
);
|
||||
if (isListener != null)
|
||||
if (dis < m_saydistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(isListener);
|
||||
}
|
||||
}
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Shout:
|
||||
if ((dis < 100) && (dis > -100))
|
||||
{
|
||||
if (li.GetChannel() == channel)
|
||||
{
|
||||
ListenerInfo isListener = m_listenerManager.IsListenerMatch(
|
||||
sourceItemID, sPart.UUID, channel, name, msg
|
||||
);
|
||||
if (isListener != null)
|
||||
if (dis < m_shoutdistance)
|
||||
{
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(isListener);
|
||||
}
|
||||
}
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ChatTypeEnum.Broadcast:
|
||||
// Dont process if this message is from itself!
|
||||
if (li.GetHostID().ToString().Equals(sourceItemID) ||
|
||||
sPart.UUID.ToString().Equals(sourceItemID))
|
||||
continue;
|
||||
|
||||
if (li.GetChannel() == channel)
|
||||
{
|
||||
ListenerInfo isListener = m_listenerManager.IsListenerMatch(
|
||||
sourceItemID, sPart.UUID, channel, name, msg
|
||||
);
|
||||
if (isListener != null)
|
||||
{
|
||||
case ChatTypeEnum.Region:
|
||||
lock (m_pending.SyncRoot)
|
||||
{
|
||||
m_pending.Enqueue(isListener);
|
||||
m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Are there any listen events ready to be dispatched?
|
||||
/// </summary>
|
||||
/// <returns>boolean indication</returns>
|
||||
public bool HasMessages()
|
||||
{
|
||||
if (m_pending != null)
|
||||
return (m_pending.Count > 0);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pop the first availlable listen event from the queue
|
||||
/// </summary>
|
||||
/// <returns>ListenerInfo with filter filled in</returns>
|
||||
public ListenerInfo GetNextMessage()
|
||||
{
|
||||
ListenerInfo li = null;
|
||||
|
@ -289,19 +309,9 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
return li;
|
||||
}
|
||||
|
||||
public uint PeekNextMessageLocalID()
|
||||
{
|
||||
return ((ListenerInfo) m_pending.Peek()).GetLocalID();
|
||||
}
|
||||
|
||||
public LLUUID PeekNextMessageItemID()
|
||||
{
|
||||
return ((ListenerInfo) m_pending.Peek()).GetItemID();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void NewClient(IClientAPI client)
|
||||
private void NewClient(IClientAPI client)
|
||||
{
|
||||
client.OnChatFromViewer += DeliverClientMessage;
|
||||
}
|
||||
|
@ -314,212 +324,267 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
|
||||
private void DeliverClientMessage(Object sender, ChatFromViewerArgs e)
|
||||
{
|
||||
DeliverMessage(e.Sender.AgentId.ToString(),
|
||||
e.Type, e.Channel,
|
||||
DeliverMessage(e.Type,
|
||||
e.Channel,
|
||||
e.Sender.FirstName + " " + e.Sender.LastName,
|
||||
e.Sender.AgentId,
|
||||
e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public class ListenerManager
|
||||
{
|
||||
//private Dictionary<int, ListenerInfo> m_listeners;
|
||||
private object ListenersLock = new object();
|
||||
private Hashtable m_listeners = Hashtable.Synchronized(new Hashtable());
|
||||
private int m_MaxListeners = 100;
|
||||
private Dictionary<int, List<ListenerInfo>> m_listeners = new Dictionary<int, List<ListenerInfo>>();
|
||||
private int m_maxlisteners;
|
||||
private int m_maxhandles;
|
||||
private int m_curlisteners;
|
||||
|
||||
public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg)
|
||||
public ListenerManager(int maxlisteners, int maxhandles)
|
||||
{
|
||||
if (m_listeners.Count < m_MaxListeners)
|
||||
{
|
||||
ListenerInfo isListener = IsListenerMatch(LLUUID.Zero.ToString(), itemID, channel, name, msg);
|
||||
m_maxlisteners = maxlisteners;
|
||||
m_maxhandles = maxhandles;
|
||||
m_curlisteners = 0;
|
||||
}
|
||||
|
||||
if (isListener == null)
|
||||
public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg)
|
||||
{
|
||||
int newHandle = GetNewHandle();
|
||||
// do we already have a match on this particular filter event?
|
||||
List<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg);
|
||||
|
||||
if (newHandle > -1)
|
||||
if (coll.Count > 0)
|
||||
{
|
||||
ListenerInfo li = new ListenerInfo(localID, newHandle, itemID, hostID, channel, name, id, msg);
|
||||
// special case, called with same filter settings, return same handle
|
||||
// (2008-05-02, tested on 1.21.1 server, still holds)
|
||||
return coll[0].GetHandle();
|
||||
}
|
||||
|
||||
lock (m_listeners.SyncRoot)
|
||||
if (m_curlisteners < m_maxlisteners)
|
||||
{
|
||||
m_listeners.Add(newHandle, li);
|
||||
int newHandle = GetNewHandle(itemID);
|
||||
|
||||
if (newHandle > 0)
|
||||
{
|
||||
ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg);
|
||||
|
||||
lock (m_listeners)
|
||||
{
|
||||
List<ListenerInfo> listeners;
|
||||
if (!m_listeners.TryGetValue(channel,out listeners))
|
||||
{
|
||||
listeners = new List<ListenerInfo>();
|
||||
m_listeners.Add(channel, listeners);
|
||||
}
|
||||
listeners.Add(li);
|
||||
m_curlisteners++;
|
||||
}
|
||||
|
||||
return newHandle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void Remove(int handle)
|
||||
public void Remove(LLUUID itemID, int handle)
|
||||
{
|
||||
lock (m_listeners.SyncRoot)
|
||||
lock (m_listeners)
|
||||
{
|
||||
m_listeners.Remove(handle);
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle))
|
||||
{
|
||||
lis.Value.Remove(li);
|
||||
if (lis.Value.Count == 0)
|
||||
{
|
||||
m_listeners.Remove(lis.Key);
|
||||
m_curlisteners--;
|
||||
}
|
||||
// there should be only one, so we bail out early
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteListener(LLUUID itemID)
|
||||
{
|
||||
ArrayList removedListeners = new ArrayList();
|
||||
List<int> emptyChannels = new List<int>();
|
||||
List<ListenerInfo> removedListeners = new List<ListenerInfo>();
|
||||
|
||||
lock (m_listeners.SyncRoot)
|
||||
lock (m_listeners)
|
||||
{
|
||||
IDictionaryEnumerator en = m_listeners.GetEnumerator();
|
||||
while (en.MoveNext())
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
ListenerInfo li = (ListenerInfo) en.Value;
|
||||
if (li.GetItemID().Equals(itemID))
|
||||
{
|
||||
removedListeners.Add(li.GetHandle());
|
||||
// store them first, else the enumerated bails on us
|
||||
removedListeners.Add(li);
|
||||
}
|
||||
}
|
||||
foreach (int handle in removedListeners)
|
||||
foreach (ListenerInfo li in removedListeners)
|
||||
{
|
||||
m_listeners.Remove(handle);
|
||||
lis.Value.Remove(li);
|
||||
m_curlisteners--;
|
||||
}
|
||||
removedListeners.Clear();
|
||||
if (lis.Value.Count == 0)
|
||||
{
|
||||
// again, store first, remove later
|
||||
emptyChannels.Add(lis.Key);
|
||||
}
|
||||
}
|
||||
foreach (int channel in emptyChannels)
|
||||
{
|
||||
m_listeners.Remove(channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetNewHandle()
|
||||
public void Activate(LLUUID itemID, int handle)
|
||||
{
|
||||
for (int i = 0; i < int.MaxValue - 1; i++)
|
||||
lock (m_listeners)
|
||||
{
|
||||
if (!m_listeners.ContainsKey(i))
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
|
||||
{
|
||||
li.Activate();
|
||||
// only one, bail out
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dectivate(LLUUID itemID, int handle)
|
||||
{
|
||||
lock (m_listeners)
|
||||
{
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
|
||||
{
|
||||
li.Deactivate();
|
||||
// only one, bail out
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non-locked access, since its always called in the context of the lock
|
||||
private int GetNewHandle(LLUUID itemID)
|
||||
{
|
||||
List<int> handles = new List<int>();
|
||||
|
||||
// build a list of used keys for this specific itemID...
|
||||
foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
|
||||
{
|
||||
foreach (ListenerInfo li in lis.Value)
|
||||
{
|
||||
if (li.GetItemID().Equals(itemID))
|
||||
handles.Add(li.GetHandle());
|
||||
}
|
||||
}
|
||||
|
||||
// Note: 0 is NOT a valid handle for llListen() to return
|
||||
for (int i = 1; i <= m_maxhandles; i++)
|
||||
{
|
||||
if (!handles.Contains(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)
|
||||
{
|
||||
if (m_listeners.ContainsKey(handle))
|
||||
{
|
||||
lock (m_listeners.SyncRoot)
|
||||
{
|
||||
ListenerInfo li = (ListenerInfo) m_listeners[handle];
|
||||
li.Activate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dectivate(int handle)
|
||||
{
|
||||
if (m_listeners.ContainsKey(handle))
|
||||
{
|
||||
ListenerInfo li = (ListenerInfo) m_listeners[handle];
|
||||
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)
|
||||
// PM2008: Ha, one could even be smart and define a specialized Enumerator.
|
||||
public List<ListenerInfo> GetListeners(LLUUID itemID, int channel, string name, LLUUID id, string msg)
|
||||
{
|
||||
bool isMatch = true;
|
||||
lock (m_listeners.SyncRoot)
|
||||
{
|
||||
IDictionaryEnumerator en = m_listeners.GetEnumerator();
|
||||
while (en.MoveNext())
|
||||
{
|
||||
ListenerInfo li = (ListenerInfo) en.Value;
|
||||
List<ListenerInfo> collection = new List<ListenerInfo>();
|
||||
|
||||
if (li.IsActive())
|
||||
lock (m_listeners)
|
||||
{
|
||||
if (li.GetHostID().Equals(listenerKey))
|
||||
List<ListenerInfo> listeners;
|
||||
if (!m_listeners.TryGetValue(channel,out listeners))
|
||||
{
|
||||
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;
|
||||
return collection;
|
||||
}
|
||||
|
||||
public ICollection GetListeners()
|
||||
foreach (ListenerInfo li in listeners)
|
||||
{
|
||||
return m_listeners.Values;
|
||||
if (!li.IsActive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!itemID.Equals(LLUUID.Zero) && !li.GetItemID().Equals(itemID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (li.GetName().Length > 0 && !li.GetName().Equals(name))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!li.GetID().Equals(LLUUID.Zero) && !li.GetID().Equals(id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
collection.Add(li);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
public class ListenerInfo
|
||||
{
|
||||
private bool m_active; // Listener is active or not
|
||||
private int m_channel; // Channel
|
||||
private int m_handle; // Assigned handle of this listener
|
||||
private LLUUID m_hostID; // ID of the host/scene part
|
||||
private LLUUID m_id; // ID to filter messages from
|
||||
private LLUUID m_itemID; // ID of the host script engine
|
||||
private uint m_localID; // Local ID from script engine
|
||||
private string m_message; // The message
|
||||
private LLUUID m_itemID; // ID of the host script engine
|
||||
private LLUUID m_hostID; // ID of the host/scene part
|
||||
private int m_channel; // Channel
|
||||
private LLUUID m_id; // ID to filter messages from
|
||||
private string m_name; // Object name to filter messages from
|
||||
private LLUUID m_sourceItemID; // ID of the scenePart or avatar source of the message
|
||||
private string m_message; // The message
|
||||
|
||||
public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message)
|
||||
public ListenerInfo(int handle, uint localID, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message)
|
||||
{
|
||||
Initialise(localID, handle, ItemID, hostID, channel, name, id, message);
|
||||
Initialise(handle, localID, 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)
|
||||
public ListenerInfo(ListenerInfo li, string name, LLUUID id, string message)
|
||||
{
|
||||
Initialise(localID, handle, ItemID, hostID, channel, name, id, message);
|
||||
m_sourceItemID = sourceItemID;
|
||||
Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message);
|
||||
}
|
||||
|
||||
private void Initialise(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name,
|
||||
private void Initialise(int handle, uint localID, LLUUID ItemID, LLUUID hostID, int channel, string name,
|
||||
LLUUID id, string message)
|
||||
{
|
||||
m_active = true;
|
||||
m_handle = handle;
|
||||
m_channel = channel;
|
||||
m_localID = localID;
|
||||
m_itemID = ItemID;
|
||||
m_hostID = hostID;
|
||||
m_channel = channel;
|
||||
m_name = name;
|
||||
m_id = id;
|
||||
m_message = message;
|
||||
m_active = true;
|
||||
m_localID = localID;
|
||||
}
|
||||
|
||||
public LLUUID GetItemID()
|
||||
|
@ -532,11 +597,6 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
|
|||
return m_hostID;
|
||||
}
|
||||
|
||||
public LLUUID GetSourceItemID()
|
||||
{
|
||||
return m_sourceItemID;
|
||||
}
|
||||
|
||||
public int GetChannel()
|
||||
{
|
||||
return m_channel;
|
||||
|
|
|
@ -465,7 +465,7 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
ChatTypeEnum.Whisper, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
|
||||
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Whisper, channelID, m_host.Name, text);
|
||||
wComm.DeliverMessage(ChatTypeEnum.Whisper, channelID, m_host.Name, m_host.UUID, text);
|
||||
}
|
||||
|
||||
public void llSay(int channelID, string text)
|
||||
|
@ -475,7 +475,7 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
ChatTypeEnum.Say, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
|
||||
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Say, channelID, m_host.Name, text);
|
||||
wComm.DeliverMessage(ChatTypeEnum.Say, channelID, m_host.Name, m_host.UUID, text);
|
||||
}
|
||||
|
||||
public void llShout(int channelID, string text)
|
||||
|
@ -485,7 +485,7 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
ChatTypeEnum.Shout, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
|
||||
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Shout, channelID, m_host.Name, text);
|
||||
wComm.DeliverMessage(ChatTypeEnum.Shout, channelID, m_host.Name, m_host.UUID, text);
|
||||
}
|
||||
|
||||
public void llRegionSay(int channelID, string text)
|
||||
|
@ -499,32 +499,30 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
m_host.AddScriptLPS(1);
|
||||
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Broadcast, channelID, m_host.Name, text);
|
||||
wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
|
||||
}
|
||||
|
||||
public int llListen(int channelID, string name, string ID, string msg)
|
||||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
if (ID == String.Empty)
|
||||
{
|
||||
ID = LLUUID.Zero.ToString();
|
||||
}
|
||||
LLUUID keyID;
|
||||
LLUUID.TryParse(ID, out keyID);
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
return wComm.Listen(m_localID, m_itemID, m_host.UUID, channelID, name, ID, msg);
|
||||
return wComm.Listen(m_localID, m_itemID, m_host.UUID, channelID, name, keyID, msg);
|
||||
}
|
||||
|
||||
public void llListenControl(int number, int active)
|
||||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.ListenControl(number, active);
|
||||
wComm.ListenControl(m_itemID, number, active);
|
||||
}
|
||||
|
||||
public void llListenRemove(int number)
|
||||
{
|
||||
m_host.AddScriptLPS(1);
|
||||
IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
wComm.ListenRemove(number);
|
||||
wComm.ListenRemove(m_itemID, number);
|
||||
}
|
||||
|
||||
public void llSensor(string name, string id, int type, double range, double arc)
|
||||
|
@ -5409,9 +5407,9 @@ namespace OpenSim.Region.ScriptEngine.Common
|
|||
// llGetOwner ==> m_host.ObjectOwner.ToString()
|
||||
llInstantMessage(m_host.ObjectOwner.ToString(),msg);
|
||||
|
||||
//World.SimChat(Helpers.StringToField(msg), ChatTypeEnum.Say, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
|
||||
//World.SimChat(Helpers.StringToField(msg), ChatTypeEnum.Owner, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
|
||||
//IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
|
||||
//wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Say, 0, m_host.Name, msg);
|
||||
//wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg);
|
||||
}
|
||||
|
||||
public void llRequestSimulatorData(string simulator, int data)
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.AsyncCommandPlugin
|
|||
{
|
||||
public class Listener
|
||||
{
|
||||
// private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
public AsyncCommandManager m_CmdManager;
|
||||
|
||||
public Listener(AsyncCommandManager CmdManager)
|
||||
|
@ -48,23 +50,24 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.AsyncCommandPlugin
|
|||
if (comms != null)
|
||||
{
|
||||
while (comms.HasMessages())
|
||||
{
|
||||
if (m_CmdManager.m_ScriptEngine.m_ScriptManager.GetScript(
|
||||
comms.PeekNextMessageLocalID(), comms.PeekNextMessageItemID()) != null)
|
||||
{
|
||||
ListenerInfo lInfo = comms.GetNextMessage();
|
||||
|
||||
if (m_CmdManager.m_ScriptEngine.m_ScriptManager.GetScript(
|
||||
lInfo.GetLocalID(), lInfo.GetItemID()) != null)
|
||||
{
|
||||
//Deliver data to prim's listen handler
|
||||
object[] resobj = new object[]
|
||||
{
|
||||
//lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage()
|
||||
new LSL_Types.LSLInteger(lInfo.GetChannel()), new LSL_Types.LSLString(lInfo.GetName()), new LSL_Types.LSLString(lInfo.GetSourceItemID().ToString()), new LSL_Types.LSLString(lInfo.GetMessage())
|
||||
new LSL_Types.LSLInteger(lInfo.GetChannel()), new LSL_Types.LSLString(lInfo.GetName()), new LSL_Types.LSLString(lInfo.GetID().ToString()), new LSL_Types.LSLString(lInfo.GetMessage())
|
||||
};
|
||||
|
||||
m_CmdManager.m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
|
||||
lInfo.GetLocalID(), lInfo.GetItemID(), "listen", EventQueueManager.llDetectNull, resobj
|
||||
);
|
||||
}
|
||||
// else
|
||||
// m_log.Info("[ScriptEngineBase.AsyncCommandPlugins: received a listen event for a (no longer) existing script ("+lInfo.GetLocalID().AsString()+")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -324,6 +324,14 @@ CleanUpOldScriptsOnStartup=true
|
|||
; Set the following to true to allow administrator owned scripts to execute console commands
|
||||
AllowosConsoleCommand=false
|
||||
|
||||
; Maximum number of llListen events we allow globally in the region
|
||||
; Set this to 0 to have no limit imposed.
|
||||
max_listens_per_region = 1000
|
||||
|
||||
; Maximum number of llListen events we allow per script
|
||||
; Set this to 0 to have no limit imposed.
|
||||
max_listens_per_script = 64
|
||||
|
||||
[DataSnapshot]
|
||||
; The following set of configs pertains to search.
|
||||
; Set index_sims to true to enable search engines to index your searchable data
|
||||
|
|
Loading…
Reference in New Issue