diff options
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs')
-rw-r--r-- | OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs | 598 |
1 files changed, 329 insertions, 269 deletions
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 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using libsecondlife; | 31 | using libsecondlife; |
31 | using Nini.Config; | 32 | using Nini.Config; |
32 | using OpenSim.Framework; | 33 | using 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 | } |