aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAdam Frisby2008-05-08 13:41:10 +0000
committerAdam Frisby2008-05-08 13:41:10 +0000
commitdc21e8d5e2c830311271ac5cafb0d166a571a74e (patch)
tree15ada49d200622454d785ccdf56012188e462cc3
parent* Commit from Jed Zhu (DeepThink) - Initial implementation of mesh collision... (diff)
downloadopensim-SC_OLD-dc21e8d5e2c830311271ac5cafb0d166a571a74e.zip
opensim-SC_OLD-dc21e8d5e2c830311271ac5cafb0d166a571a74e.tar.gz
opensim-SC_OLD-dc21e8d5e2c830311271ac5cafb0d166a571a74e.tar.bz2
opensim-SC_OLD-dc21e8d5e2c830311271ac5cafb0d166a571a74e.tar.xz
* Applying patch #1121 - Fixes for llListen() (Thanks Middlelink!)
-rw-r--r--OpenSim/Framework/IClientAPI.cs2
-rw-r--r--OpenSim/Region/Environment/Interfaces/IWorldComm.cs10
-rw-r--r--OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs598
-rw-r--r--OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs24
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncCommandPlugins/Listener.cs11
-rw-r--r--bin/OpenSim.ini.example8
6 files changed, 361 insertions, 292 deletions
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index c40cf66..60b49ab 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -55,6 +55,8 @@ namespace OpenSim.Framework
55 StartTyping = 4, 55 StartTyping = 4,
56 StopTyping = 5, 56 StopTyping = 5,
57 DebugChannel = 6, 57 DebugChannel = 6,
58 Region = 7,
59 Owner = 8,
58 Broadcast = 0xFF 60 Broadcast = 0xFF
59 } 61 }
60 62
diff --git a/OpenSim/Region/Environment/Interfaces/IWorldComm.cs b/OpenSim/Region/Environment/Interfaces/IWorldComm.cs
index 8a63c54..c471e7f 100644
--- a/OpenSim/Region/Environment/Interfaces/IWorldComm.cs
+++ b/OpenSim/Region/Environment/Interfaces/IWorldComm.cs
@@ -33,14 +33,12 @@ namespace OpenSim.Region.Environment.Interfaces
33{ 33{
34 public interface IWorldComm 34 public interface IWorldComm
35 { 35 {
36 int Listen(uint LocalID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg); 36 int Listen(uint LocalID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg);
37 void DeliverMessage(string sourceItemID, ChatTypeEnum type, int channel, string name, string msg); 37 void DeliverMessage(ChatTypeEnum type, int channel, string name, LLUUID id, string msg);
38 bool HasMessages(); 38 bool HasMessages();
39 ListenerInfo GetNextMessage(); 39 ListenerInfo GetNextMessage();
40 void ListenControl(int handle, int active); 40 void ListenControl(LLUUID itemID, int handle, int active);
41 void ListenRemove(int handle); 41 void ListenRemove(LLUUID itemID, int handle);
42 void DeleteListener(LLUUID itemID); 42 void DeleteListener(LLUUID itemID);
43 uint PeekNextMessageLocalID();
44 LLUUID PeekNextMessageItemID();
45 } 43 }
46} 44}
diff --git a/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs
index e79b2bd..9121f7a 100644
--- a/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs
+++ b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using libsecondlife; 31using libsecondlife;
31using Nini.Config; 32using Nini.Config;
32using OpenSim.Framework; 33using OpenSim.Framework;
@@ -60,6 +61,21 @@ using OpenSim.Region.Environment.Scenes;
60 * be used to prevent in-world repeater loops. However, the 61 * be used to prevent in-world repeater loops. However, the
61 * linden functions do not have this capability, so for now 62 * linden functions do not have this capability, so for now
62 * thats the way it works. 63 * thats the way it works.
64 * Instead it blocks messages originating from the same prim.
65 * (not Object!)
66 *
67 * For LSL compliance, note the following:
68 * (Tested again 1.21.1 on May 2, 2008)
69 * 1. 'id' has to be parsed into a LLUUID. None-UUID keys are
70 * to be replaced by the ZeroID key. (Well, TryParse does
71 * that for us.
72 * 2. Setting up an listen event from the same script, with the
73 * same filter settings (including step 1), returns the same
74 * handle as the original filter.
75 * 3. (TODO) handles should be script-local. Starting from 1.
76 * Might be actually easier to map the global handle into
77 * script-local handle in the ScriptEngine. Not sure if its
78 * worth the effort tho.
63 * 79 *
64 * **************************************************/ 80 * **************************************************/
65 81
@@ -67,25 +83,39 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
67{ 83{
68 public class WorldCommModule : IRegionModule, IWorldComm 84 public class WorldCommModule : IRegionModule, IWorldComm
69 { 85 {
70 private object CommListLock = new object();
71 private object ListLock = new object();
72 private ListenerManager m_listenerManager; 86 private ListenerManager m_listenerManager;
73 private string m_name = "WorldCommModule";
74 private Queue m_pending; 87 private Queue m_pending;
75 private Queue m_pendingQ; 88 private Queue m_pendingQ;
76 private Scene m_scene; 89 private Scene m_scene;
77 90 private int m_whisperdistance = 10;
78 public WorldCommModule() 91 private int m_saydistance = 30;
79 { 92 private int m_shoutdistance = 100;
80 }
81 93
82 #region IRegionModule Members 94 #region IRegionModule Members
83 95
84 public void Initialise(Scene scene, IConfigSource config) 96 public void Initialise(Scene scene, IConfigSource config)
85 { 97 {
98 // wrap this in a try block so that defaults will work if
99 // the config file doesn't specify otherwise.
100 int maxlisteners = 1000;
101 int maxhandles = 64;
102 try
103 {
104 m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance);
105 m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance);
106 m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance);
107 maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners);
108 maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles);
109 }
110 catch (Exception)
111 {
112 }
113 if (maxlisteners < 1) maxlisteners = int.MaxValue;
114 if (maxhandles < 1) maxhandles = int.MaxValue;
115
86 m_scene = scene; 116 m_scene = scene;
87 m_scene.RegisterModuleInterface<IWorldComm>(this); 117 m_scene.RegisterModuleInterface<IWorldComm>(this);
88 m_listenerManager = new ListenerManager(); 118 m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
89 m_scene.EventManager.OnNewClient += NewClient; 119 m_scene.EventManager.OnNewClient += NewClient;
90 m_pendingQ = new Queue(); 120 m_pendingQ = new Queue();
91 m_pending = Queue.Synchronized(m_pendingQ); 121 m_pending = Queue.Synchronized(m_pendingQ);
@@ -101,7 +131,7 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
101 131
102 public string Name 132 public string Name
103 { 133 {
104 get { return m_name; } 134 get { return "WorldCommModule"; }
105 } 135 }
106 136
107 public bool IsSharedModule 137 public bool IsSharedModule
@@ -113,170 +143,160 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
113 143
114 #region IWorldComm Members 144 #region IWorldComm Members
115 145
116 public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) 146 /// <summary>
147 /// Create a listen event callback with the specified filters.
148 /// The parameters localID,itemID are needed to uniquely identify
149 /// the script during 'peek' time. Parameter hostID is needed to
150 /// determine the position of the script.
151 /// </summary>
152 /// <param name="localID">localID of the script engine</param>
153 /// <param name="itemID">UUID of the script engine</param>
154 /// <param name="hostID">UUID of the SceneObjectPart</param>
155 /// <param name="channel">channel to listen on</param>
156 /// <param name="name">name to filter on</param>
157 /// <param name="id">key to filter on (user given, could be totally faked)</param>
158 /// <param name="msg">msg to filter on</param>
159 /// <returns>number of the scripts handle</returns>
160 public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg)
117 { 161 {
118 return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); 162 return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
119 } 163 }
120 164
121 public void ListenControl(int handle, int active) 165 /// <summary>
166 /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
167 /// The handle used is returned from Listen()
168 /// </summary>
169 /// <param name="itemID">UUID of the script engine</param>
170 /// <param name="handle">handle returned by Listen()</param>
171 /// <param name="active">temp. activate or deactivate the Listen()</param>
172 public void ListenControl(LLUUID itemID, int handle, int active)
122 { 173 {
123 if (m_listenerManager != null) 174 if (active == 1)
124 { 175 m_listenerManager.Activate(itemID, handle);
125 if (active == 1) 176 else if (active == 0)
126 m_listenerManager.Activate(handle); 177 m_listenerManager.Dectivate(itemID, handle);
127 else if (active == 0)
128 m_listenerManager.Dectivate(handle);
129 }
130 } 178 }
131 179
132 public void ListenRemove(int handle) 180 /// <summary>
181 /// Removes the listen event callback with handle
182 /// </summary>
183 /// <param name="itemID">UUID of the script engine</param>
184 /// <param name="handle">handle returned by Listen()</param>
185 public void ListenRemove(LLUUID itemID, int handle)
133 { 186 {
134 if (m_listenerManager != null) 187 m_listenerManager.Remove(itemID, handle);
135 {
136 m_listenerManager.Remove(handle);
137 }
138 } 188 }
139 189
190 /// <summary>
191 /// Removes all listen event callbacks for the given itemID
192 /// (script engine)
193 /// </summary>
194 /// <param name="itemID">UUID of the script engine</param>
140 public void DeleteListener(LLUUID itemID) 195 public void DeleteListener(LLUUID itemID)
141 { 196 {
142 if (m_listenerManager != null) 197 m_listenerManager.DeleteListener(itemID);
143 {
144 m_listenerManager.DeleteListener(itemID);
145 }
146 } 198 }
147 199
148 // This method scans nearby objects and determines if they are listeners, 200 /// <summary>
149 // and if so if this message fits the filter. If it does, then 201 /// This method scans over the objects which registered an interest in listen callbacks.
150 // enqueue the message for delivery to the objects listen event handler. 202 /// For everyone it finds, it checks if it fits the given filter. If it does, then
151 // Objects that do an llSay have their messages delivered here, and for 203 /// enqueue the message for delivery to the objects listen event handler.
152 // nearby avatars, the SimChat function is used. 204 /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
153 public void DeliverMessage(string sourceItemID, ChatTypeEnum type, int channel, string name, string msg) 205 /// Objects that do an llSay have their messages delivered here and for nearby avatars,
206 /// the OnChatFromViewer event is used.
207 /// </summary>
208 /// <param name="type">type of delvery (whisper,say,shout or regionwide)</param>
209 /// <param name="channel">channel to sent on</param>
210 /// <param name="name">name of sender (object or avatar)</param>
211 /// <param name="id">key of sender (object or avatar)</param>
212 /// <param name="msg">msg to sent</param>
213 public void DeliverMessage(ChatTypeEnum type, int channel, string name, LLUUID id, string msg)
154 { 214 {
155 SceneObjectPart source = null; 215 SceneObjectPart source = null;
156 ScenePresence avatar = null; 216 ScenePresence avatar = null;
157 217 LLVector3 position;
158 source = m_scene.GetSceneObjectPart(new LLUUID(sourceItemID)); 218
159 if (source == null) 219 source = m_scene.GetSceneObjectPart(id);
160 { 220 if (source != null)
161 avatar = m_scene.GetScenePresence(new LLUUID(sourceItemID)); 221 position = source.AbsolutePosition;
222 else {
223 avatar = m_scene.GetScenePresence(id);
224 if (avatar != null)
225 position = avatar.AbsolutePosition;
226 else
227 // bail out early, given source could not be found
228 return;
162 } 229 }
163 if ((avatar != null) || (source != null)) 230
231 // Determine which listen event filters match the given set of arguments, this results
232 // in a limited set of listeners, each belonging a host. If the host is in range, add them
233 // to the pending queue.
234 foreach (ListenerInfo li in m_listenerManager.GetListeners(LLUUID.Zero, channel, name, id, msg))
164 { 235 {
165 // Loop through the objects in the scene 236 // Dont process if this message is from yourself!
166 // If they are in proximity, then if they are 237 if (li.GetHostID().Equals(id))
167 // listeners, if so add them to the pending queue 238 continue;
168 239
169 foreach (ListenerInfo li in m_listenerManager.GetListeners()) 240 SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID());
170 { 241 if (sPart == null)
171 EntityBase sPart; 242 continue;
172 243
173 m_scene.Entities.TryGetValue(li.GetHostID(), out sPart); 244 double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position);
245 switch (type)
246 {
247 case ChatTypeEnum.Whisper:
248 if (dis < m_whisperdistance)
249 {
250 lock (m_pending.SyncRoot)
251 {
252 m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
253 }
254 }
255 break;
174 256
175 if (sPart != null) 257 case ChatTypeEnum.Say:
176 { 258 if (dis < m_saydistance)
177 double dis = 0; 259 {
260 lock (m_pending.SyncRoot)
261 {
262 m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
263 }
264 }
265 break;
178 266
179 if (source != null) 267 case ChatTypeEnum.Shout:
180 dis = Util.GetDistanceTo(sPart.AbsolutePosition, source.AbsolutePosition); 268 if (dis < m_shoutdistance)
181 else 269 {
182 dis = Util.GetDistanceTo(sPart.AbsolutePosition, avatar.AbsolutePosition); 270 lock (m_pending.SyncRoot)
271 {
272 m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
273 }
274 }
275 break;
183 276
184 switch (type) 277 case ChatTypeEnum.Region:
278 lock (m_pending.SyncRoot)
185 { 279 {
186 case ChatTypeEnum.Whisper: 280 m_pending.Enqueue(new ListenerInfo(li,name,id,msg));
187
188 if ((dis < 10) && (dis > -10))
189 {
190 if (li.GetChannel() == channel)
191 {
192 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
193 sourceItemID, sPart.UUID, channel, name, msg
194 );
195 if (isListener != null)
196 {
197 lock (m_pending.SyncRoot)
198 {
199 m_pending.Enqueue(isListener);
200 }
201 }
202 }
203 }
204 break;
205
206 case ChatTypeEnum.Say:
207
208 if ((dis < 30) && (dis > -30))
209 {
210 if (li.GetChannel() == channel)
211 {
212 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
213 sourceItemID, sPart.UUID, channel, name, msg
214 );
215 if (isListener != null)
216 {
217 lock (m_pending.SyncRoot)
218 {
219 m_pending.Enqueue(isListener);
220 }
221 }
222 }
223 }
224 break;
225
226 case ChatTypeEnum.Shout:
227 if ((dis < 100) && (dis > -100))
228 {
229 if (li.GetChannel() == channel)
230 {
231 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
232 sourceItemID, sPart.UUID, channel, name, msg
233 );
234 if (isListener != null)
235 {
236 lock (m_pending.SyncRoot)
237 {
238 m_pending.Enqueue(isListener);
239 }
240 }
241 }
242 }
243 break;
244
245 case ChatTypeEnum.Broadcast:
246 // Dont process if this message is from itself!
247 if (li.GetHostID().ToString().Equals(sourceItemID) ||
248 sPart.UUID.ToString().Equals(sourceItemID))
249 continue;
250
251 if (li.GetChannel() == channel)
252 {
253 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
254 sourceItemID, sPart.UUID, channel, name, msg
255 );
256 if (isListener != null)
257 {
258 lock (m_pending.SyncRoot)
259 {
260 m_pending.Enqueue(isListener);
261 }
262 }
263 }
264
265 break;
266 } 281 }
267 } 282 break;
268 } 283 }
269 } 284 }
270 } 285 }
271 286
287 /// <summary>
288 /// Are there any listen events ready to be dispatched?
289 /// </summary>
290 /// <returns>boolean indication</returns>
272 public bool HasMessages() 291 public bool HasMessages()
273 { 292 {
274 if (m_pending != null) 293 return (m_pending.Count > 0);
275 return (m_pending.Count > 0);
276 else
277 return false;
278 } 294 }
279 295
296 /// <summary>
297 /// Pop the first availlable listen event from the queue
298 /// </summary>
299 /// <returns>ListenerInfo with filter filled in</returns>
280 public ListenerInfo GetNextMessage() 300 public ListenerInfo GetNextMessage()
281 { 301 {
282 ListenerInfo li = null; 302 ListenerInfo li = null;
@@ -289,19 +309,9 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
289 return li; 309 return li;
290 } 310 }
291 311
292 public uint PeekNextMessageLocalID()
293 {
294 return ((ListenerInfo) m_pending.Peek()).GetLocalID();
295 }
296
297 public LLUUID PeekNextMessageItemID()
298 {
299 return ((ListenerInfo) m_pending.Peek()).GetItemID();
300 }
301
302 #endregion 312 #endregion
303 313
304 public void NewClient(IClientAPI client) 314 private void NewClient(IClientAPI client)
305 { 315 {
306 client.OnChatFromViewer += DeliverClientMessage; 316 client.OnChatFromViewer += DeliverClientMessage;
307 } 317 }
@@ -314,212 +324,267 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
314 324
315 private void DeliverClientMessage(Object sender, ChatFromViewerArgs e) 325 private void DeliverClientMessage(Object sender, ChatFromViewerArgs e)
316 { 326 {
317 DeliverMessage(e.Sender.AgentId.ToString(), 327 DeliverMessage(e.Type,
318 e.Type, e.Channel, 328 e.Channel,
319 e.Sender.FirstName + " " + e.Sender.LastName, 329 e.Sender.FirstName + " " + e.Sender.LastName,
330 e.Sender.AgentId,
320 e.Message); 331 e.Message);
321 } 332 }
322 } 333 }
323 334
324 public class ListenerManager 335 public class ListenerManager
325 { 336 {
326 //private Dictionary<int, ListenerInfo> m_listeners; 337 private Dictionary<int, List<ListenerInfo>> m_listeners = new Dictionary<int, List<ListenerInfo>>();
327 private object ListenersLock = new object(); 338 private int m_maxlisteners;
328 private Hashtable m_listeners = Hashtable.Synchronized(new Hashtable()); 339 private int m_maxhandles;
329 private int m_MaxListeners = 100; 340 private int m_curlisteners;
341
342 public ListenerManager(int maxlisteners, int maxhandles)
343 {
344 m_maxlisteners = maxlisteners;
345 m_maxhandles = maxhandles;
346 m_curlisteners = 0;
347 }
330 348
331 public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) 349 public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, LLUUID id, string msg)
332 { 350 {
333 if (m_listeners.Count < m_MaxListeners) 351 // do we already have a match on this particular filter event?
352 List<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg);
353
354 if (coll.Count > 0)
355 {
356 // special case, called with same filter settings, return same handle
357 // (2008-05-02, tested on 1.21.1 server, still holds)
358 return coll[0].GetHandle();
359 }
360
361 if (m_curlisteners < m_maxlisteners)
334 { 362 {
335 ListenerInfo isListener = IsListenerMatch(LLUUID.Zero.ToString(), itemID, channel, name, msg); 363 int newHandle = GetNewHandle(itemID);
336 364
337 if (isListener == null) 365 if (newHandle > 0)
338 { 366 {
339 int newHandle = GetNewHandle(); 367 ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg);
340 368
341 if (newHandle > -1) 369 lock (m_listeners)
342 { 370 {
343 ListenerInfo li = new ListenerInfo(localID, newHandle, itemID, hostID, channel, name, id, msg); 371 List<ListenerInfo> listeners;
344 372 if (!m_listeners.TryGetValue(channel,out listeners))
345 lock (m_listeners.SyncRoot)
346 { 373 {
347 m_listeners.Add(newHandle, li); 374 listeners = new List<ListenerInfo>();
375 m_listeners.Add(channel, listeners);
348 } 376 }
349 377 listeners.Add(li);
350 return newHandle; 378 m_curlisteners++;
351 } 379 }
380
381 return newHandle;
352 } 382 }
353 } 383 }
354
355 return -1; 384 return -1;
356 } 385 }
357 386
358 public void Remove(int handle) 387 public void Remove(LLUUID itemID, int handle)
359 { 388 {
360 lock (m_listeners.SyncRoot) 389 lock (m_listeners)
361 { 390 {
362 m_listeners.Remove(handle); 391 foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
392 {
393 foreach (ListenerInfo li in lis.Value)
394 {
395 if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle))
396 {
397 lis.Value.Remove(li);
398 if (lis.Value.Count == 0)
399 {
400 m_listeners.Remove(lis.Key);
401 m_curlisteners--;
402 }
403 // there should be only one, so we bail out early
404 return;
405 }
406 }
407 }
363 } 408 }
364 } 409 }
365 410
366 public void DeleteListener(LLUUID itemID) 411 public void DeleteListener(LLUUID itemID)
367 { 412 {
368 ArrayList removedListeners = new ArrayList(); 413 List<int> emptyChannels = new List<int>();
414 List<ListenerInfo> removedListeners = new List<ListenerInfo>();
369 415
370 lock (m_listeners.SyncRoot) 416 lock (m_listeners)
371 { 417 {
372 IDictionaryEnumerator en = m_listeners.GetEnumerator(); 418 foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
373 while (en.MoveNext())
374 { 419 {
375 ListenerInfo li = (ListenerInfo) en.Value; 420 foreach (ListenerInfo li in lis.Value)
376 if (li.GetItemID().Equals(itemID)) 421 {
422 if (li.GetItemID().Equals(itemID))
423 {
424 // store them first, else the enumerated bails on us
425 removedListeners.Add(li);
426 }
427 }
428 foreach (ListenerInfo li in removedListeners)
429 {
430 lis.Value.Remove(li);
431 m_curlisteners--;
432 }
433 removedListeners.Clear();
434 if (lis.Value.Count == 0)
377 { 435 {
378 removedListeners.Add(li.GetHandle()); 436 // again, store first, remove later
437 emptyChannels.Add(lis.Key);
379 } 438 }
380 } 439 }
381 foreach (int handle in removedListeners) 440 foreach (int channel in emptyChannels)
382 { 441 {
383 m_listeners.Remove(handle); 442 m_listeners.Remove(channel);
384 } 443 }
385 } 444 }
386 } 445 }
387 446
388 private int GetNewHandle() 447 public void Activate(LLUUID itemID, int handle)
389 { 448 {
390 for (int i = 0; i < int.MaxValue - 1; i++) 449 lock (m_listeners)
391 { 450 {
392 if (!m_listeners.ContainsKey(i)) 451 foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
393 return i; 452 {
453 foreach (ListenerInfo li in lis.Value)
454 {
455 if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
456 {
457 li.Activate();
458 // only one, bail out
459 return;
460 }
461 }
462 }
394 } 463 }
395
396 return -1;
397 } 464 }
398 465
399 public bool IsListener(LLUUID hostID) 466 public void Dectivate(LLUUID itemID, int handle)
400 { 467 {
401 foreach (ListenerInfo li in m_listeners.Values) 468 lock (m_listeners)
402 { 469 {
403 if (li.GetHostID().Equals(hostID)) 470 foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
404 return true; 471 {
472 foreach (ListenerInfo li in lis.Value)
473 {
474 if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle)
475 {
476 li.Deactivate();
477 // only one, bail out
478 return;
479 }
480 }
481 }
405 } 482 }
406
407 return false;
408 } 483 }
409 484
410 public void Activate(int handle) 485 // non-locked access, since its always called in the context of the lock
486 private int GetNewHandle(LLUUID itemID)
411 { 487 {
412 if (m_listeners.ContainsKey(handle)) 488 List<int> handles = new List<int>();
489
490 // build a list of used keys for this specific itemID...
491 foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners)
413 { 492 {
414 lock (m_listeners.SyncRoot) 493 foreach (ListenerInfo li in lis.Value)
415 { 494 {
416 ListenerInfo li = (ListenerInfo) m_listeners[handle]; 495 if (li.GetItemID().Equals(itemID))
417 li.Activate(); 496 handles.Add(li.GetHandle());
418 } 497 }
419 } 498 }
420 }
421 499
422 public void Dectivate(int handle) 500 // Note: 0 is NOT a valid handle for llListen() to return
423 { 501 for (int i = 1; i <= m_maxhandles; i++)
424 if (m_listeners.ContainsKey(handle))
425 { 502 {
426 ListenerInfo li = (ListenerInfo) m_listeners[handle]; 503 if (!handles.Contains(i))
427 li.Deactivate(); 504 return i;
428 } 505 }
506
507 return -1;
429 } 508 }
430 509
431 // Theres probably a more clever and efficient way to 510 // Theres probably a more clever and efficient way to
432 // do this, maybe with regex. 511 // do this, maybe with regex.
433 public ListenerInfo IsListenerMatch(string sourceItemID, LLUUID listenerKey, int channel, string name, 512 // PM2008: Ha, one could even be smart and define a specialized Enumerator.
434 string msg) 513 public List<ListenerInfo> GetListeners(LLUUID itemID, int channel, string name, LLUUID id, string msg)
435 { 514 {
436 bool isMatch = true; 515 List<ListenerInfo> collection = new List<ListenerInfo>();
437 lock (m_listeners.SyncRoot) 516
517 lock (m_listeners)
438 { 518 {
439 IDictionaryEnumerator en = m_listeners.GetEnumerator(); 519 List<ListenerInfo> listeners;
440 while (en.MoveNext()) 520 if (!m_listeners.TryGetValue(channel,out listeners))
441 { 521 {
442 ListenerInfo li = (ListenerInfo) en.Value; 522 return collection;
523 }
443 524
444 if (li.IsActive()) 525 foreach (ListenerInfo li in listeners)
526 {
527 if (!li.IsActive())
445 { 528 {
446 if (li.GetHostID().Equals(listenerKey)) 529 continue;
447 { 530 }
448 if (channel == li.GetChannel()) 531 if (!itemID.Equals(LLUUID.Zero) && !li.GetItemID().Equals(itemID))
449 { 532 {
450 if ((li.GetID().ToString().Length > 0) && 533 continue;
451 (!li.GetID().Equals(LLUUID.Zero))) 534 }
452 { 535 if (li.GetName().Length > 0 && !li.GetName().Equals(name))
453 if (!li.GetID().ToString().Equals(sourceItemID)) 536 {
454 { 537 continue;
455 isMatch = false; 538 }
456 } 539 if (!li.GetID().Equals(LLUUID.Zero) && !li.GetID().Equals(id))
457 } 540 {
458 if (isMatch && (li.GetName().Length > 0)) 541 continue;
459 { 542 }
460 if (li.GetName().Equals(name)) 543 if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg))
461 { 544 {
462 isMatch = false; 545 continue;
463 }
464 }
465 if (isMatch)
466 {
467 return new ListenerInfo(
468 li.GetLocalID(), li.GetHandle(), li.GetItemID(), li.GetHostID(),
469 li.GetChannel(), name, li.GetID(), msg, new LLUUID(sourceItemID)
470 );
471 }
472 }
473 }
474 } 546 }
547 collection.Add(li);
475 } 548 }
476 } 549 }
477 return null; 550 return collection;
478 }
479
480 public ICollection GetListeners()
481 {
482 return m_listeners.Values;
483 } 551 }
484 } 552 }
485 553
486 public class ListenerInfo 554 public class ListenerInfo
487 { 555 {
488 private bool m_active; // Listener is active or not 556 private bool m_active; // Listener is active or not
489 private int m_channel; // Channel
490 private int m_handle; // Assigned handle of this listener 557 private int m_handle; // Assigned handle of this listener
558 private uint m_localID; // Local ID from script engine
559 private LLUUID m_itemID; // ID of the host script engine
491 private LLUUID m_hostID; // ID of the host/scene part 560 private LLUUID m_hostID; // ID of the host/scene part
561 private int m_channel; // Channel
492 private LLUUID m_id; // ID to filter messages from 562 private LLUUID m_id; // ID to filter messages from
493 private LLUUID m_itemID; // ID of the host script engine
494 private uint m_localID; // Local ID from script engine
495 private string m_message; // The message
496 private string m_name; // Object name to filter messages from 563 private string m_name; // Object name to filter messages from
497 private LLUUID m_sourceItemID; // ID of the scenePart or avatar source of the message 564 private string m_message; // The message
498 565
499 public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message) 566 public ListenerInfo(int handle, uint localID, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message)
500 { 567 {
501 Initialise(localID, handle, ItemID, hostID, channel, name, id, message); 568 Initialise(handle, localID, ItemID, hostID, channel, name, id, message);
502 } 569 }
503 570
504 public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, 571 public ListenerInfo(ListenerInfo li, string name, LLUUID id, string message)
505 string message, LLUUID sourceItemID)
506 { 572 {
507 Initialise(localID, handle, ItemID, hostID, channel, name, id, message); 573 Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message);
508 m_sourceItemID = sourceItemID;
509 } 574 }
510 575
511 private void Initialise(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, 576 private void Initialise(int handle, uint localID, LLUUID ItemID, LLUUID hostID, int channel, string name,
512 LLUUID id, string message) 577 LLUUID id, string message)
513 { 578 {
579 m_active = true;
514 m_handle = handle; 580 m_handle = handle;
515 m_channel = channel; 581 m_localID = localID;
516 m_itemID = ItemID; 582 m_itemID = ItemID;
517 m_hostID = hostID; 583 m_hostID = hostID;
584 m_channel = channel;
518 m_name = name; 585 m_name = name;
519 m_id = id; 586 m_id = id;
520 m_message = message; 587 m_message = message;
521 m_active = true;
522 m_localID = localID;
523 } 588 }
524 589
525 public LLUUID GetItemID() 590 public LLUUID GetItemID()
@@ -532,11 +597,6 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
532 return m_hostID; 597 return m_hostID;
533 } 598 }
534 599
535 public LLUUID GetSourceItemID()
536 {
537 return m_sourceItemID;
538 }
539
540 public int GetChannel() 600 public int GetChannel()
541 { 601 {
542 return m_channel; 602 return m_channel;
@@ -582,4 +642,4 @@ namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
582 return m_id; 642 return m_id;
583 } 643 }
584 } 644 }
585} \ No newline at end of file 645}
diff --git a/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs b/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs
index 5c705b3..805a575 100644
--- a/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs
+++ b/OpenSim/Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs
@@ -465,7 +465,7 @@ namespace OpenSim.Region.ScriptEngine.Common
465 ChatTypeEnum.Whisper, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID); 465 ChatTypeEnum.Whisper, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
466 466
467 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 467 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
468 wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Whisper, channelID, m_host.Name, text); 468 wComm.DeliverMessage(ChatTypeEnum.Whisper, channelID, m_host.Name, m_host.UUID, text);
469 } 469 }
470 470
471 public void llSay(int channelID, string text) 471 public void llSay(int channelID, string text)
@@ -475,7 +475,7 @@ namespace OpenSim.Region.ScriptEngine.Common
475 ChatTypeEnum.Say, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID); 475 ChatTypeEnum.Say, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
476 476
477 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 477 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
478 wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Say, channelID, m_host.Name, text); 478 wComm.DeliverMessage(ChatTypeEnum.Say, channelID, m_host.Name, m_host.UUID, text);
479 } 479 }
480 480
481 public void llShout(int channelID, string text) 481 public void llShout(int channelID, string text)
@@ -485,7 +485,7 @@ namespace OpenSim.Region.ScriptEngine.Common
485 ChatTypeEnum.Shout, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID); 485 ChatTypeEnum.Shout, channelID, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
486 486
487 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 487 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
488 wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Shout, channelID, m_host.Name, text); 488 wComm.DeliverMessage(ChatTypeEnum.Shout, channelID, m_host.Name, m_host.UUID, text);
489 } 489 }
490 490
491 public void llRegionSay(int channelID, string text) 491 public void llRegionSay(int channelID, string text)
@@ -499,32 +499,30 @@ namespace OpenSim.Region.ScriptEngine.Common
499 m_host.AddScriptLPS(1); 499 m_host.AddScriptLPS(1);
500 500
501 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 501 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
502 wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Broadcast, channelID, m_host.Name, text); 502 wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text);
503 } 503 }
504 504
505 public int llListen(int channelID, string name, string ID, string msg) 505 public int llListen(int channelID, string name, string ID, string msg)
506 { 506 {
507 m_host.AddScriptLPS(1); 507 m_host.AddScriptLPS(1);
508 if (ID == String.Empty) 508 LLUUID keyID;
509 { 509 LLUUID.TryParse(ID, out keyID);
510 ID = LLUUID.Zero.ToString();
511 }
512 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 510 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
513 return wComm.Listen(m_localID, m_itemID, m_host.UUID, channelID, name, ID, msg); 511 return wComm.Listen(m_localID, m_itemID, m_host.UUID, channelID, name, keyID, msg);
514 } 512 }
515 513
516 public void llListenControl(int number, int active) 514 public void llListenControl(int number, int active)
517 { 515 {
518 m_host.AddScriptLPS(1); 516 m_host.AddScriptLPS(1);
519 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 517 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
520 wComm.ListenControl(number, active); 518 wComm.ListenControl(m_itemID, number, active);
521 } 519 }
522 520
523 public void llListenRemove(int number) 521 public void llListenRemove(int number)
524 { 522 {
525 m_host.AddScriptLPS(1); 523 m_host.AddScriptLPS(1);
526 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 524 IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
527 wComm.ListenRemove(number); 525 wComm.ListenRemove(m_itemID, number);
528 } 526 }
529 527
530 public void llSensor(string name, string id, int type, double range, double arc) 528 public void llSensor(string name, string id, int type, double range, double arc)
@@ -5409,9 +5407,9 @@ namespace OpenSim.Region.ScriptEngine.Common
5409 // llGetOwner ==> m_host.ObjectOwner.ToString() 5407 // llGetOwner ==> m_host.ObjectOwner.ToString()
5410 llInstantMessage(m_host.ObjectOwner.ToString(),msg); 5408 llInstantMessage(m_host.ObjectOwner.ToString(),msg);
5411 5409
5412 //World.SimChat(Helpers.StringToField(msg), ChatTypeEnum.Say, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID); 5410 //World.SimChat(Helpers.StringToField(msg), ChatTypeEnum.Owner, 0, m_host.AbsolutePosition, m_host.Name, m_host.UUID);
5413 //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>(); 5411 //IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
5414 //wComm.DeliverMessage(m_host.UUID.ToString(), ChatTypeEnum.Say, 0, m_host.Name, msg); 5412 //wComm.DeliverMessage(ChatTypeEnum.Owner, 0, m_host.Name, m_host.UUID, msg);
5415 } 5413 }
5416 5414
5417 public void llRequestSimulatorData(string simulator, int data) 5415 public void llRequestSimulatorData(string simulator, int data)
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncCommandPlugins/Listener.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncCommandPlugins/Listener.cs
index 55dbabf..6a1235b 100644
--- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncCommandPlugins/Listener.cs
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AsyncCommandPlugins/Listener.cs
@@ -32,6 +32,8 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.AsyncCommandPlugin
32{ 32{
33 public class Listener 33 public class Listener
34 { 34 {
35 // private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
36
35 public AsyncCommandManager m_CmdManager; 37 public AsyncCommandManager m_CmdManager;
36 38
37 public Listener(AsyncCommandManager CmdManager) 39 public Listener(AsyncCommandManager CmdManager)
@@ -49,22 +51,23 @@ namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.AsyncCommandPlugin
49 { 51 {
50 while (comms.HasMessages()) 52 while (comms.HasMessages())
51 { 53 {
54 ListenerInfo lInfo = comms.GetNextMessage();
52 if (m_CmdManager.m_ScriptEngine.m_ScriptManager.GetScript( 55 if (m_CmdManager.m_ScriptEngine.m_ScriptManager.GetScript(
53 comms.PeekNextMessageLocalID(), comms.PeekNextMessageItemID()) != null) 56 lInfo.GetLocalID(), lInfo.GetItemID()) != null)
54 { 57 {
55 ListenerInfo lInfo = comms.GetNextMessage();
56
57 //Deliver data to prim's listen handler 58 //Deliver data to prim's listen handler
58 object[] resobj = new object[] 59 object[] resobj = new object[]
59 { 60 {
60 //lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage() 61 //lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage()
61 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()) 62 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())
62 }; 63 };
63 64
64 m_CmdManager.m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( 65 m_CmdManager.m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
65 lInfo.GetLocalID(), lInfo.GetItemID(), "listen", EventQueueManager.llDetectNull, resobj 66 lInfo.GetLocalID(), lInfo.GetItemID(), "listen", EventQueueManager.llDetectNull, resobj
66 ); 67 );
67 } 68 }
69 // else
70 // m_log.Info("[ScriptEngineBase.AsyncCommandPlugins: received a listen event for a (no longer) existing script ("+lInfo.GetLocalID().AsString()+")");
68 } 71 }
69 } 72 }
70 } 73 }
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 2736405..2725bd0 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -324,6 +324,14 @@ CleanUpOldScriptsOnStartup=true
324; Set the following to true to allow administrator owned scripts to execute console commands 324; Set the following to true to allow administrator owned scripts to execute console commands
325AllowosConsoleCommand=false 325AllowosConsoleCommand=false
326 326
327; Maximum number of llListen events we allow globally in the region
328; Set this to 0 to have no limit imposed.
329max_listens_per_region = 1000
330
331; Maximum number of llListen events we allow per script
332; Set this to 0 to have no limit imposed.
333max_listens_per_script = 64
334
327[DataSnapshot] 335[DataSnapshot]
328; The following set of configs pertains to search. 336; The following set of configs pertains to search.
329; Set index_sims to true to enable search engines to index your searchable data 337; Set index_sims to true to enable search engines to index your searchable data