diff options
author | Dr Scofield | 2009-02-10 13:10:57 +0000 |
---|---|---|
committer | Dr Scofield | 2009-02-10 13:10:57 +0000 |
commit | 180be7de07014aa33bc6066f12a0819b731c1c9d (patch) | |
tree | 3aa13af3cda4b808fa9453655875327699b61311 /OpenSim/Region/CoreModules/Scripting/WorldComm | |
parent | Stopgap measure: To use gridlaunch, or GUI, start opensim with (diff) | |
download | opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.zip opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.gz opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.bz2 opensim-SC_OLD-180be7de07014aa33bc6066f12a0819b731c1c9d.tar.xz |
this is step 2 of 2 of the OpenSim.Region.Environment refactor.
NOTHING has been deleted or moved off to forge at this point. what
has happened is that OpenSim.Region.Environment.Modules has been split
in two:
- OpenSim.Region.CoreModules: all those modules that are either
directly or indirectly referenced from other OpenSim packages, or
that provide functionality that the OpenSim developer community
considers core functionality:
CoreModules/Agent/AssetTransaction
CoreModules/Agent/Capabilities
CoreModules/Agent/TextureDownload
CoreModules/Agent/TextureSender
CoreModules/Agent/TextureSender/Tests
CoreModules/Agent/Xfer
CoreModules/Avatar/AvatarFactory
CoreModules/Avatar/Chat/ChatModule
CoreModules/Avatar/Combat
CoreModules/Avatar/Currency/SampleMoney
CoreModules/Avatar/Dialog
CoreModules/Avatar/Friends
CoreModules/Avatar/Gestures
CoreModules/Avatar/Groups
CoreModules/Avatar/InstantMessage
CoreModules/Avatar/Inventory
CoreModules/Avatar/Inventory/Archiver
CoreModules/Avatar/Inventory/Transfer
CoreModules/Avatar/Lure
CoreModules/Avatar/ObjectCaps
CoreModules/Avatar/Profiles
CoreModules/Communications/Local
CoreModules/Communications/REST
CoreModules/Framework/EventQueue
CoreModules/Framework/InterfaceCommander
CoreModules/Hypergrid
CoreModules/InterGrid
CoreModules/Scripting/DynamicTexture
CoreModules/Scripting/EMailModules
CoreModules/Scripting/HttpRequest
CoreModules/Scripting/LoadImageURL
CoreModules/Scripting/VectorRender
CoreModules/Scripting/WorldComm
CoreModules/Scripting/XMLRPC
CoreModules/World/Archiver
CoreModules/World/Archiver/Tests
CoreModules/World/Estate
CoreModules/World/Land
CoreModules/World/Permissions
CoreModules/World/Serialiser
CoreModules/World/Sound
CoreModules/World/Sun
CoreModules/World/Terrain
CoreModules/World/Terrain/DefaultEffects
CoreModules/World/Terrain/DefaultEffects/bin
CoreModules/World/Terrain/DefaultEffects/bin/Debug
CoreModules/World/Terrain/Effects
CoreModules/World/Terrain/FileLoaders
CoreModules/World/Terrain/FloodBrushes
CoreModules/World/Terrain/PaintBrushes
CoreModules/World/Terrain/Tests
CoreModules/World/Vegetation
CoreModules/World/Wind
CoreModules/World/WorldMap
- OpenSim.Region.OptionalModules: all those modules that are not core
modules:
OptionalModules/Avatar/Chat/IRC-stuff
OptionalModules/Avatar/Concierge
OptionalModules/Avatar/Voice/AsterixVoice
OptionalModules/Avatar/Voice/SIPVoice
OptionalModules/ContentManagementSystem
OptionalModules/Grid/Interregion
OptionalModules/Python
OptionalModules/SvnSerialiser
OptionalModules/World/NPC
OptionalModules/World/TreePopulator
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/WorldComm')
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs | 726 |
1 files changed, 726 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs new file mode 100644 index 0000000..c363940 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs | |||
@@ -0,0 +1,726 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSim Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using OpenMetaverse; | ||
32 | using Nini.Config; | ||
33 | using OpenSim.Framework; | ||
34 | using OpenSim.Region.Framework.Interfaces; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | |||
37 | // using log4net; | ||
38 | // using System.Reflection; | ||
39 | |||
40 | |||
41 | /***************************************************** | ||
42 | * | ||
43 | * WorldCommModule | ||
44 | * | ||
45 | * | ||
46 | * Holding place for world comms - basically llListen | ||
47 | * function implementation. | ||
48 | * | ||
49 | * lLListen(integer channel, string name, key id, string msg) | ||
50 | * The name, id, and msg arguments specify the filtering | ||
51 | * criteria. You can pass the empty string | ||
52 | * (or NULL_KEY for id) for these to set a completely | ||
53 | * open filter; this causes the listen() event handler to be | ||
54 | * invoked for all chat on the channel. To listen only | ||
55 | * for chat spoken by a specific object or avatar, | ||
56 | * specify the name and/or id arguments. To listen | ||
57 | * only for a specific command, specify the | ||
58 | * (case-sensitive) msg argument. If msg is not empty, | ||
59 | * listener will only hear strings which are exactly equal | ||
60 | * to msg. You can also use all the arguments to establish | ||
61 | * the most restrictive filtering criteria. | ||
62 | * | ||
63 | * It might be useful for each listener to maintain a message | ||
64 | * digest, with a list of recent messages by UUID. This can | ||
65 | * be used to prevent in-world repeater loops. However, the | ||
66 | * linden functions do not have this capability, so for now | ||
67 | * thats the way it works. | ||
68 | * Instead it blocks messages originating from the same prim. | ||
69 | * (not Object!) | ||
70 | * | ||
71 | * For LSL compliance, note the following: | ||
72 | * (Tested again 1.21.1 on May 2, 2008) | ||
73 | * 1. 'id' has to be parsed into a UUID. None-UUID keys are | ||
74 | * to be replaced by the ZeroID key. (Well, TryParse does | ||
75 | * that for us. | ||
76 | * 2. Setting up an listen event from the same script, with the | ||
77 | * same filter settings (including step 1), returns the same | ||
78 | * handle as the original filter. | ||
79 | * 3. (TODO) handles should be script-local. Starting from 1. | ||
80 | * Might be actually easier to map the global handle into | ||
81 | * script-local handle in the ScriptEngine. Not sure if its | ||
82 | * worth the effort tho. | ||
83 | * | ||
84 | * **************************************************/ | ||
85 | |||
86 | namespace OpenSim.Region.CoreModules.Scripting.WorldComm | ||
87 | { | ||
88 | public class WorldCommModule : IRegionModule, IWorldComm | ||
89 | { | ||
90 | // private static readonly ILog m_log = | ||
91 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
92 | |||
93 | private ListenerManager m_listenerManager; | ||
94 | private Queue m_pending; | ||
95 | private Queue m_pendingQ; | ||
96 | private Scene m_scene; | ||
97 | private int m_whisperdistance = 10; | ||
98 | private int m_saydistance = 30; | ||
99 | private int m_shoutdistance = 100; | ||
100 | |||
101 | #region IRegionModule Members | ||
102 | |||
103 | public void Initialise(Scene scene, IConfigSource config) | ||
104 | { | ||
105 | // wrap this in a try block so that defaults will work if | ||
106 | // the config file doesn't specify otherwise. | ||
107 | int maxlisteners = 1000; | ||
108 | int maxhandles = 64; | ||
109 | try | ||
110 | { | ||
111 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); | ||
112 | m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); | ||
113 | m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); | ||
114 | maxlisteners = config.Configs["Chat"].GetInt("max_listens_per_region", maxlisteners); | ||
115 | maxhandles = config.Configs["Chat"].GetInt("max_listens_per_script", maxhandles); | ||
116 | } | ||
117 | catch (Exception) | ||
118 | { | ||
119 | } | ||
120 | if (maxlisteners < 1) maxlisteners = int.MaxValue; | ||
121 | if (maxhandles < 1) maxhandles = int.MaxValue; | ||
122 | |||
123 | m_scene = scene; | ||
124 | m_scene.RegisterModuleInterface<IWorldComm>(this); | ||
125 | m_listenerManager = new ListenerManager(maxlisteners, maxhandles); | ||
126 | m_scene.EventManager.OnChatFromClient += DeliverClientMessage; | ||
127 | m_scene.EventManager.OnChatBroadcast += DeliverClientMessage; | ||
128 | m_pendingQ = new Queue(); | ||
129 | m_pending = Queue.Synchronized(m_pendingQ); | ||
130 | } | ||
131 | |||
132 | public void PostInitialise() | ||
133 | { | ||
134 | } | ||
135 | |||
136 | public void Close() | ||
137 | { | ||
138 | } | ||
139 | |||
140 | public string Name | ||
141 | { | ||
142 | get { return "WorldCommModule"; } | ||
143 | } | ||
144 | |||
145 | public bool IsSharedModule | ||
146 | { | ||
147 | get { return false; } | ||
148 | } | ||
149 | |||
150 | #endregion | ||
151 | |||
152 | #region IWorldComm Members | ||
153 | |||
154 | /// <summary> | ||
155 | /// Create a listen event callback with the specified filters. | ||
156 | /// The parameters localID,itemID are needed to uniquely identify | ||
157 | /// the script during 'peek' time. Parameter hostID is needed to | ||
158 | /// determine the position of the script. | ||
159 | /// </summary> | ||
160 | /// <param name="localID">localID of the script engine</param> | ||
161 | /// <param name="itemID">UUID of the script engine</param> | ||
162 | /// <param name="hostID">UUID of the SceneObjectPart</param> | ||
163 | /// <param name="channel">channel to listen on</param> | ||
164 | /// <param name="name">name to filter on</param> | ||
165 | /// <param name="id">key to filter on (user given, could be totally faked)</param> | ||
166 | /// <param name="msg">msg to filter on</param> | ||
167 | /// <returns>number of the scripts handle</returns> | ||
168 | public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) | ||
169 | { | ||
170 | return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); | ||
171 | } | ||
172 | |||
173 | /// <summary> | ||
174 | /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE). | ||
175 | /// The handle used is returned from Listen() | ||
176 | /// </summary> | ||
177 | /// <param name="itemID">UUID of the script engine</param> | ||
178 | /// <param name="handle">handle returned by Listen()</param> | ||
179 | /// <param name="active">temp. activate or deactivate the Listen()</param> | ||
180 | public void ListenControl(UUID itemID, int handle, int active) | ||
181 | { | ||
182 | if (active == 1) | ||
183 | m_listenerManager.Activate(itemID, handle); | ||
184 | else if (active == 0) | ||
185 | m_listenerManager.Dectivate(itemID, handle); | ||
186 | } | ||
187 | |||
188 | /// <summary> | ||
189 | /// Removes the listen event callback with handle | ||
190 | /// </summary> | ||
191 | /// <param name="itemID">UUID of the script engine</param> | ||
192 | /// <param name="handle">handle returned by Listen()</param> | ||
193 | public void ListenRemove(UUID itemID, int handle) | ||
194 | { | ||
195 | m_listenerManager.Remove(itemID, handle); | ||
196 | } | ||
197 | |||
198 | /// <summary> | ||
199 | /// Removes all listen event callbacks for the given itemID | ||
200 | /// (script engine) | ||
201 | /// </summary> | ||
202 | /// <param name="itemID">UUID of the script engine</param> | ||
203 | public void DeleteListener(UUID itemID) | ||
204 | { | ||
205 | m_listenerManager.DeleteListener(itemID); | ||
206 | } | ||
207 | |||
208 | |||
209 | protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20); | ||
210 | |||
211 | public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg) | ||
212 | { | ||
213 | Vector3 position; | ||
214 | SceneObjectPart source; | ||
215 | ScenePresence avatar; | ||
216 | |||
217 | if ((source = m_scene.GetSceneObjectPart(id)) != null) | ||
218 | position = source.AbsolutePosition; | ||
219 | else if ((avatar = m_scene.GetScenePresence(id)) != null) | ||
220 | position = avatar.AbsolutePosition; | ||
221 | else if (ChatTypeEnum.Region == type) | ||
222 | position = CenterOfRegion; | ||
223 | else | ||
224 | return; | ||
225 | |||
226 | DeliverMessage(type, channel, name, id, msg, position); | ||
227 | } | ||
228 | |||
229 | /// <summary> | ||
230 | /// This method scans over the objects which registered an interest in listen callbacks. | ||
231 | /// For everyone it finds, it checks if it fits the given filter. If it does, then | ||
232 | /// enqueue the message for delivery to the objects listen event handler. | ||
233 | /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values. | ||
234 | /// Objects that do an llSay have their messages delivered here and for nearby avatars, | ||
235 | /// the OnChatFromClient event is used. | ||
236 | /// </summary> | ||
237 | /// <param name="type">type of delvery (whisper,say,shout or regionwide)</param> | ||
238 | /// <param name="channel">channel to sent on</param> | ||
239 | /// <param name="name">name of sender (object or avatar)</param> | ||
240 | /// <param name="id">key of sender (object or avatar)</param> | ||
241 | /// <param name="msg">msg to sent</param> | ||
242 | public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg, Vector3 position) | ||
243 | { | ||
244 | // m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}", | ||
245 | // type, channel, name, id, msg); | ||
246 | |||
247 | // Determine which listen event filters match the given set of arguments, this results | ||
248 | // in a limited set of listeners, each belonging a host. If the host is in range, add them | ||
249 | // to the pending queue. | ||
250 | foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg)) | ||
251 | { | ||
252 | // Dont process if this message is from yourself! | ||
253 | if (li.GetHostID().Equals(id)) | ||
254 | continue; | ||
255 | |||
256 | SceneObjectPart sPart = m_scene.GetSceneObjectPart(li.GetHostID()); | ||
257 | if (sPart == null) | ||
258 | continue; | ||
259 | |||
260 | double dis = Util.GetDistanceTo(sPart.AbsolutePosition, position); | ||
261 | switch (type) | ||
262 | { | ||
263 | case ChatTypeEnum.Whisper: | ||
264 | if (dis < m_whisperdistance) | ||
265 | { | ||
266 | lock (m_pending.SyncRoot) | ||
267 | { | ||
268 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
269 | } | ||
270 | } | ||
271 | break; | ||
272 | |||
273 | case ChatTypeEnum.Say: | ||
274 | if (dis < m_saydistance) | ||
275 | { | ||
276 | lock (m_pending.SyncRoot) | ||
277 | { | ||
278 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
279 | } | ||
280 | } | ||
281 | break; | ||
282 | |||
283 | case ChatTypeEnum.Shout: | ||
284 | if (dis < m_shoutdistance) | ||
285 | { | ||
286 | lock (m_pending.SyncRoot) | ||
287 | { | ||
288 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
289 | } | ||
290 | } | ||
291 | break; | ||
292 | |||
293 | case ChatTypeEnum.Region: | ||
294 | lock (m_pending.SyncRoot) | ||
295 | { | ||
296 | m_pending.Enqueue(new ListenerInfo(li,name,id,msg)); | ||
297 | } | ||
298 | break; | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | |||
303 | /// <summary> | ||
304 | /// Are there any listen events ready to be dispatched? | ||
305 | /// </summary> | ||
306 | /// <returns>boolean indication</returns> | ||
307 | public bool HasMessages() | ||
308 | { | ||
309 | return (m_pending.Count > 0); | ||
310 | } | ||
311 | |||
312 | /// <summary> | ||
313 | /// Pop the first availlable listen event from the queue | ||
314 | /// </summary> | ||
315 | /// <returns>ListenerInfo with filter filled in</returns> | ||
316 | public IWorldCommListenerInfo GetNextMessage() | ||
317 | { | ||
318 | ListenerInfo li = null; | ||
319 | |||
320 | lock (m_pending.SyncRoot) | ||
321 | { | ||
322 | li = (ListenerInfo) m_pending.Dequeue(); | ||
323 | } | ||
324 | |||
325 | return li; | ||
326 | } | ||
327 | |||
328 | #endregion | ||
329 | |||
330 | /******************************************************************** | ||
331 | * | ||
332 | * Listener Stuff | ||
333 | * | ||
334 | * *****************************************************************/ | ||
335 | |||
336 | private void DeliverClientMessage(Object sender, OSChatMessage e) | ||
337 | { | ||
338 | if (null != e.Sender) | ||
339 | DeliverMessage(e.Type, e.Channel, e.Sender.Name, e.Sender.AgentId, e.Message, e.Position); | ||
340 | else | ||
341 | DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero, e.Message, e.Position); | ||
342 | } | ||
343 | |||
344 | public Object[] GetSerializationData(UUID itemID) | ||
345 | { | ||
346 | return m_listenerManager.GetSerializationData(itemID); | ||
347 | } | ||
348 | |||
349 | public void CreateFromData(uint localID, UUID itemID, UUID hostID, | ||
350 | Object[] data) | ||
351 | { | ||
352 | m_listenerManager.AddFromData(localID, itemID, hostID, data); | ||
353 | } | ||
354 | } | ||
355 | |||
356 | public class ListenerManager | ||
357 | { | ||
358 | private Dictionary<int, List<ListenerInfo>> m_listeners = new Dictionary<int, List<ListenerInfo>>(); | ||
359 | private int m_maxlisteners; | ||
360 | private int m_maxhandles; | ||
361 | private int m_curlisteners; | ||
362 | |||
363 | public ListenerManager(int maxlisteners, int maxhandles) | ||
364 | { | ||
365 | m_maxlisteners = maxlisteners; | ||
366 | m_maxhandles = maxhandles; | ||
367 | m_curlisteners = 0; | ||
368 | } | ||
369 | |||
370 | public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) | ||
371 | { | ||
372 | // do we already have a match on this particular filter event? | ||
373 | List<ListenerInfo> coll = GetListeners(itemID, channel, name, id, msg); | ||
374 | |||
375 | if (coll.Count > 0) | ||
376 | { | ||
377 | // special case, called with same filter settings, return same handle | ||
378 | // (2008-05-02, tested on 1.21.1 server, still holds) | ||
379 | return coll[0].GetHandle(); | ||
380 | } | ||
381 | |||
382 | if (m_curlisteners < m_maxlisteners) | ||
383 | { | ||
384 | int newHandle = GetNewHandle(itemID); | ||
385 | |||
386 | if (newHandle > 0) | ||
387 | { | ||
388 | ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg); | ||
389 | |||
390 | lock (m_listeners) | ||
391 | { | ||
392 | List<ListenerInfo> listeners; | ||
393 | if (!m_listeners.TryGetValue(channel,out listeners)) | ||
394 | { | ||
395 | listeners = new List<ListenerInfo>(); | ||
396 | m_listeners.Add(channel, listeners); | ||
397 | } | ||
398 | listeners.Add(li); | ||
399 | m_curlisteners++; | ||
400 | } | ||
401 | |||
402 | return newHandle; | ||
403 | } | ||
404 | } | ||
405 | return -1; | ||
406 | } | ||
407 | |||
408 | public void Remove(UUID itemID, int handle) | ||
409 | { | ||
410 | lock (m_listeners) | ||
411 | { | ||
412 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
413 | { | ||
414 | foreach (ListenerInfo li in lis.Value) | ||
415 | { | ||
416 | if (li.GetItemID().Equals(itemID) && li.GetHandle().Equals(handle)) | ||
417 | { | ||
418 | lis.Value.Remove(li); | ||
419 | if (lis.Value.Count == 0) | ||
420 | { | ||
421 | m_listeners.Remove(lis.Key); | ||
422 | m_curlisteners--; | ||
423 | } | ||
424 | // there should be only one, so we bail out early | ||
425 | return; | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | public void DeleteListener(UUID itemID) | ||
433 | { | ||
434 | List<int> emptyChannels = new List<int>(); | ||
435 | List<ListenerInfo> removedListeners = new List<ListenerInfo>(); | ||
436 | |||
437 | lock (m_listeners) | ||
438 | { | ||
439 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
440 | { | ||
441 | foreach (ListenerInfo li in lis.Value) | ||
442 | { | ||
443 | if (li.GetItemID().Equals(itemID)) | ||
444 | { | ||
445 | // store them first, else the enumerated bails on us | ||
446 | removedListeners.Add(li); | ||
447 | } | ||
448 | } | ||
449 | foreach (ListenerInfo li in removedListeners) | ||
450 | { | ||
451 | lis.Value.Remove(li); | ||
452 | m_curlisteners--; | ||
453 | } | ||
454 | removedListeners.Clear(); | ||
455 | if (lis.Value.Count == 0) | ||
456 | { | ||
457 | // again, store first, remove later | ||
458 | emptyChannels.Add(lis.Key); | ||
459 | } | ||
460 | } | ||
461 | foreach (int channel in emptyChannels) | ||
462 | { | ||
463 | m_listeners.Remove(channel); | ||
464 | } | ||
465 | } | ||
466 | } | ||
467 | |||
468 | public void Activate(UUID itemID, int handle) | ||
469 | { | ||
470 | lock (m_listeners) | ||
471 | { | ||
472 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
473 | { | ||
474 | foreach (ListenerInfo li in lis.Value) | ||
475 | { | ||
476 | if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle) | ||
477 | { | ||
478 | li.Activate(); | ||
479 | // only one, bail out | ||
480 | return; | ||
481 | } | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | } | ||
486 | |||
487 | public void Dectivate(UUID itemID, int handle) | ||
488 | { | ||
489 | lock (m_listeners) | ||
490 | { | ||
491 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
492 | { | ||
493 | foreach (ListenerInfo li in lis.Value) | ||
494 | { | ||
495 | if (li.GetItemID().Equals(itemID) && li.GetHandle() == handle) | ||
496 | { | ||
497 | li.Deactivate(); | ||
498 | // only one, bail out | ||
499 | return; | ||
500 | } | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | } | ||
505 | |||
506 | // non-locked access, since its always called in the context of the lock | ||
507 | private int GetNewHandle(UUID itemID) | ||
508 | { | ||
509 | List<int> handles = new List<int>(); | ||
510 | |||
511 | // build a list of used keys for this specific itemID... | ||
512 | foreach (KeyValuePair<int,List<ListenerInfo>> lis in m_listeners) | ||
513 | { | ||
514 | foreach (ListenerInfo li in lis.Value) | ||
515 | { | ||
516 | if (li.GetItemID().Equals(itemID)) | ||
517 | handles.Add(li.GetHandle()); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | // Note: 0 is NOT a valid handle for llListen() to return | ||
522 | for (int i = 1; i <= m_maxhandles; i++) | ||
523 | { | ||
524 | if (!handles.Contains(i)) | ||
525 | return i; | ||
526 | } | ||
527 | |||
528 | return -1; | ||
529 | } | ||
530 | |||
531 | // Theres probably a more clever and efficient way to | ||
532 | // do this, maybe with regex. | ||
533 | // PM2008: Ha, one could even be smart and define a specialized Enumerator. | ||
534 | public List<ListenerInfo> GetListeners(UUID itemID, int channel, string name, UUID id, string msg) | ||
535 | { | ||
536 | List<ListenerInfo> collection = new List<ListenerInfo>(); | ||
537 | |||
538 | lock (m_listeners) | ||
539 | { | ||
540 | List<ListenerInfo> listeners; | ||
541 | if (!m_listeners.TryGetValue(channel,out listeners)) | ||
542 | { | ||
543 | return collection; | ||
544 | } | ||
545 | |||
546 | foreach (ListenerInfo li in listeners) | ||
547 | { | ||
548 | if (!li.IsActive()) | ||
549 | { | ||
550 | continue; | ||
551 | } | ||
552 | if (!itemID.Equals(UUID.Zero) && !li.GetItemID().Equals(itemID)) | ||
553 | { | ||
554 | continue; | ||
555 | } | ||
556 | if (li.GetName().Length > 0 && !li.GetName().Equals(name)) | ||
557 | { | ||
558 | continue; | ||
559 | } | ||
560 | if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id)) | ||
561 | { | ||
562 | continue; | ||
563 | } | ||
564 | if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg)) | ||
565 | { | ||
566 | continue; | ||
567 | } | ||
568 | collection.Add(li); | ||
569 | } | ||
570 | } | ||
571 | return collection; | ||
572 | } | ||
573 | |||
574 | public Object[] GetSerializationData(UUID itemID) | ||
575 | { | ||
576 | List<Object> data = new List<Object>(); | ||
577 | |||
578 | foreach (List<ListenerInfo> list in m_listeners.Values) | ||
579 | { | ||
580 | foreach (ListenerInfo l in list) | ||
581 | { | ||
582 | if (l.GetItemID() == itemID) | ||
583 | data.AddRange(l.GetSerializationData()); | ||
584 | } | ||
585 | } | ||
586 | return (Object[])data.ToArray(); | ||
587 | } | ||
588 | |||
589 | public void AddFromData(uint localID, UUID itemID, UUID hostID, | ||
590 | Object[] data) | ||
591 | { | ||
592 | int idx = 0; | ||
593 | Object[] item = new Object[6]; | ||
594 | |||
595 | while (idx < data.Length) | ||
596 | { | ||
597 | Array.Copy(data, idx, item, 0, 6); | ||
598 | |||
599 | ListenerInfo info = | ||
600 | ListenerInfo.FromData(localID, itemID, hostID, item); | ||
601 | |||
602 | if (!m_listeners.ContainsKey((int)item[2])) | ||
603 | m_listeners.Add((int)item[2], new List<ListenerInfo>()); | ||
604 | m_listeners[(int)item[2]].Add(info); | ||
605 | |||
606 | idx+=6; | ||
607 | } | ||
608 | } | ||
609 | } | ||
610 | |||
611 | public class ListenerInfo: IWorldCommListenerInfo | ||
612 | { | ||
613 | private bool m_active; // Listener is active or not | ||
614 | private int m_handle; // Assigned handle of this listener | ||
615 | private uint m_localID; // Local ID from script engine | ||
616 | private UUID m_itemID; // ID of the host script engine | ||
617 | private UUID m_hostID; // ID of the host/scene part | ||
618 | private int m_channel; // Channel | ||
619 | private UUID m_id; // ID to filter messages from | ||
620 | private string m_name; // Object name to filter messages from | ||
621 | private string m_message; // The message | ||
622 | |||
623 | public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message) | ||
624 | { | ||
625 | Initialise(handle, localID, ItemID, hostID, channel, name, id, message); | ||
626 | } | ||
627 | |||
628 | public ListenerInfo(ListenerInfo li, string name, UUID id, string message) | ||
629 | { | ||
630 | Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message); | ||
631 | } | ||
632 | |||
633 | private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, | ||
634 | UUID id, string message) | ||
635 | { | ||
636 | m_active = true; | ||
637 | m_handle = handle; | ||
638 | m_localID = localID; | ||
639 | m_itemID = ItemID; | ||
640 | m_hostID = hostID; | ||
641 | m_channel = channel; | ||
642 | m_name = name; | ||
643 | m_id = id; | ||
644 | m_message = message; | ||
645 | } | ||
646 | |||
647 | public Object[] GetSerializationData() | ||
648 | { | ||
649 | Object[] data = new Object[6]; | ||
650 | |||
651 | data[0] = m_active; | ||
652 | data[1] = m_handle; | ||
653 | data[2] = m_channel; | ||
654 | data[3] = m_name; | ||
655 | data[4] = m_id; | ||
656 | data[5] = m_message; | ||
657 | |||
658 | return data; | ||
659 | } | ||
660 | |||
661 | public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data) | ||
662 | { | ||
663 | ListenerInfo linfo = new ListenerInfo((int)data[1], localID, | ||
664 | ItemID, hostID, (int)data[2], (string)data[3], | ||
665 | (UUID)data[4], (string)data[5]); | ||
666 | linfo.m_active=(bool)data[0]; | ||
667 | |||
668 | return linfo; | ||
669 | } | ||
670 | |||
671 | public UUID GetItemID() | ||
672 | { | ||
673 | return m_itemID; | ||
674 | } | ||
675 | |||
676 | public UUID GetHostID() | ||
677 | { | ||
678 | return m_hostID; | ||
679 | } | ||
680 | |||
681 | public int GetChannel() | ||
682 | { | ||
683 | return m_channel; | ||
684 | } | ||
685 | |||
686 | public uint GetLocalID() | ||
687 | { | ||
688 | return m_localID; | ||
689 | } | ||
690 | |||
691 | public int GetHandle() | ||
692 | { | ||
693 | return m_handle; | ||
694 | } | ||
695 | |||
696 | public string GetMessage() | ||
697 | { | ||
698 | return m_message; | ||
699 | } | ||
700 | |||
701 | public string GetName() | ||
702 | { | ||
703 | return m_name; | ||
704 | } | ||
705 | |||
706 | public bool IsActive() | ||
707 | { | ||
708 | return m_active; | ||
709 | } | ||
710 | |||
711 | public void Deactivate() | ||
712 | { | ||
713 | m_active = false; | ||
714 | } | ||
715 | |||
716 | public void Activate() | ||
717 | { | ||
718 | m_active = true; | ||
719 | } | ||
720 | |||
721 | public UUID GetID() | ||
722 | { | ||
723 | return m_id; | ||
724 | } | ||
725 | } | ||
726 | } | ||