clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 OpenSimulator 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 System.Text.RegularExpressions;
32  
33 using Nini.Config;
34 using Mono.Addins;
35  
36 using OpenMetaverse;
37  
38 using OpenSim.Framework;
39 using OpenSim.Region.Framework.Interfaces;
40 using OpenSim.Region.Framework.Scenes;
41  
42 // using log4net;
43 // using System.Reflection;
44  
45  
46 /*****************************************************
47 *
48 * WorldCommModule
49 *
50 *
51 * Holding place for world comms - basically llListen
52 * function implementation.
53 *
54 * lLListen(integer channel, string name, key id, string msg)
55 * The name, id, and msg arguments specify the filtering
56 * criteria. You can pass the empty string
57 * (or NULL_KEY for id) for these to set a completely
58 * open filter; this causes the listen() event handler to be
59 * invoked for all chat on the channel. To listen only
60 * for chat spoken by a specific object or avatar,
61 * specify the name and/or id arguments. To listen
62 * only for a specific command, specify the
63 * (case-sensitive) msg argument. If msg is not empty,
64 * listener will only hear strings which are exactly equal
65 * to msg. You can also use all the arguments to establish
66 * the most restrictive filtering criteria.
67 *
68 * It might be useful for each listener to maintain a message
69 * digest, with a list of recent messages by UUID. This can
70 * be used to prevent in-world repeater loops. However, the
71 * linden functions do not have this capability, so for now
72 * thats the way it works.
73 * Instead it blocks messages originating from the same prim.
74 * (not Object!)
75 *
76 * For LSL compliance, note the following:
77 * (Tested again 1.21.1 on May 2, 2008)
78 * 1. 'id' has to be parsed into a UUID. None-UUID keys are
79 * to be replaced by the ZeroID key. (Well, TryParse does
80 * that for us.
81 * 2. Setting up an listen event from the same script, with the
82 * same filter settings (including step 1), returns the same
83 * handle as the original filter.
84 * 3. (TODO) handles should be script-local. Starting from 1.
85 * Might be actually easier to map the global handle into
86 * script-local handle in the ScriptEngine. Not sure if its
87 * worth the effort tho.
88 *
89 * **************************************************/
90  
91 namespace OpenSim.Region.CoreModules.Scripting.WorldComm
92 {
93 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WorldCommModule")]
94 public class WorldCommModule : IWorldComm, INonSharedRegionModule
95 {
96 // private static readonly ILog m_log =
97 // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
98  
99 private ListenerManager m_listenerManager;
100 private Queue m_pending;
101 private Queue m_pendingQ;
102 private Scene m_scene;
103 private int m_whisperdistance = 10;
104 private int m_saydistance = 20;
105 private int m_shoutdistance = 100;
106  
107 #region INonSharedRegionModule Members
108  
109 public void Initialise(IConfigSource config)
110 {
111 // wrap this in a try block so that defaults will work if
112 // the config file doesn't specify otherwise.
113 int maxlisteners = 1000;
114 int maxhandles = 64;
115 try
116 {
117 m_whisperdistance = config.Configs["Chat"].GetInt(
118 "whisper_distance", m_whisperdistance);
119 m_saydistance = config.Configs["Chat"].GetInt(
120 "say_distance", m_saydistance);
121 m_shoutdistance = config.Configs["Chat"].GetInt(
122 "shout_distance", m_shoutdistance);
123 maxlisteners = config.Configs["LL-Functions"].GetInt(
124 "max_listens_per_region", maxlisteners);
125 maxhandles = config.Configs["LL-Functions"].GetInt(
126 "max_listens_per_script", maxhandles);
127 }
128 catch (Exception)
129 {
130 }
131 if (maxlisteners < 1) maxlisteners = int.MaxValue;
132 if (maxhandles < 1) maxhandles = int.MaxValue;
133 m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
134 m_pendingQ = new Queue();
135 m_pending = Queue.Synchronized(m_pendingQ);
136 }
137  
138 public void PostInitialise()
139 {
140 }
141  
142 public void AddRegion(Scene scene)
143 {
144 m_scene = scene;
145 m_scene.RegisterModuleInterface<IWorldComm>(this);
146 m_scene.EventManager.OnChatFromClient += DeliverClientMessage;
147 m_scene.EventManager.OnChatBroadcast += DeliverClientMessage;
148 }
149  
150 public void RegionLoaded(Scene scene) { }
151  
152 public void RemoveRegion(Scene scene)
153 {
154 if (scene != m_scene)
155 return;
156  
157 m_scene.UnregisterModuleInterface<IWorldComm>(this);
158 m_scene.EventManager.OnChatBroadcast -= DeliverClientMessage;
159 m_scene.EventManager.OnChatBroadcast -= DeliverClientMessage;
160 }
161  
162 public void Close()
163 {
164 }
165  
166 public string Name
167 {
168 get { return "WorldCommModule"; }
169 }
170  
171 public Type ReplaceableInterface { get { return null; } }
172  
173 #endregion
174  
175 #region IWorldComm Members
176  
177 public int ListenerCount
178 {
179 get
180 {
181 return m_listenerManager.ListenerCount;
182 }
183 }
184  
185 /// <summary>
186 /// Create a listen event callback with the specified filters.
187 /// The parameters localID,itemID are needed to uniquely identify
188 /// the script during 'peek' time. Parameter hostID is needed to
189 /// determine the position of the script.
190 /// </summary>
191 /// <param name="localID">localID of the script engine</param>
192 /// <param name="itemID">UUID of the script engine</param>
193 /// <param name="hostID">UUID of the SceneObjectPart</param>
194 /// <param name="channel">channel to listen on</param>
195 /// <param name="name">name to filter on</param>
196 /// <param name="id">
197 /// key to filter on (user given, could be totally faked)
198 /// </param>
199 /// <param name="msg">msg to filter on</param>
200 /// <returns>number of the scripts handle</returns>
201 public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
202 string name, UUID id, string msg)
203 {
204 return m_listenerManager.AddListener(localID, itemID, hostID,
205 channel, name, id, msg);
206 }
207  
208 /// <summary>
209 /// Create a listen event callback with the specified filters.
210 /// The parameters localID,itemID are needed to uniquely identify
211 /// the script during 'peek' time. Parameter hostID is needed to
212 /// determine the position of the script.
213 /// </summary>
214 /// <param name="localID">localID of the script engine</param>
215 /// <param name="itemID">UUID of the script engine</param>
216 /// <param name="hostID">UUID of the SceneObjectPart</param>
217 /// <param name="channel">channel to listen on</param>
218 /// <param name="name">name to filter on</param>
219 /// <param name="id">
220 /// key to filter on (user given, could be totally faked)
221 /// </param>
222 /// <param name="msg">msg to filter on</param>
223 /// <param name="regexBitfield">
224 /// Bitfield indicating which strings should be processed as regex.
225 /// </param>
226 /// <returns>number of the scripts handle</returns>
227 public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
228 string name, UUID id, string msg, int regexBitfield)
229 {
230 return m_listenerManager.AddListener(localID, itemID, hostID,
231 channel, name, id, msg, regexBitfield);
232 }
233  
234 /// <summary>
235 /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
236 /// The handle used is returned from Listen()
237 /// </summary>
238 /// <param name="itemID">UUID of the script engine</param>
239 /// <param name="handle">handle returned by Listen()</param>
240 /// <param name="active">temp. activate or deactivate the Listen()</param>
241 public void ListenControl(UUID itemID, int handle, int active)
242 {
243 if (active == 1)
244 m_listenerManager.Activate(itemID, handle);
245 else if (active == 0)
246 m_listenerManager.Dectivate(itemID, handle);
247 }
248  
249 /// <summary>
250 /// Removes the listen event callback with handle
251 /// </summary>
252 /// <param name="itemID">UUID of the script engine</param>
253 /// <param name="handle">handle returned by Listen()</param>
254 public void ListenRemove(UUID itemID, int handle)
255 {
256 m_listenerManager.Remove(itemID, handle);
257 }
258  
259 /// <summary>
260 /// Removes all listen event callbacks for the given itemID
261 /// (script engine)
262 /// </summary>
263 /// <param name="itemID">UUID of the script engine</param>
264 public void DeleteListener(UUID itemID)
265 {
266 m_listenerManager.DeleteListener(itemID);
267 }
268  
269  
270 protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20);
271  
272 public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg)
273 {
274 Vector3 position;
275 SceneObjectPart source;
276 ScenePresence avatar;
277  
278 if ((source = m_scene.GetSceneObjectPart(id)) != null)
279 position = source.AbsolutePosition;
280 else if ((avatar = m_scene.GetScenePresence(id)) != null)
281 position = avatar.AbsolutePosition;
282 else if (ChatTypeEnum.Region == type)
283 position = CenterOfRegion;
284 else
285 return;
286  
287 DeliverMessage(type, channel, name, id, msg, position);
288 }
289  
290 /// <summary>
291 /// This method scans over the objects which registered an interest in listen callbacks.
292 /// For everyone it finds, it checks if it fits the given filter. If it does, then
293 /// enqueue the message for delivery to the objects listen event handler.
294 /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
295 /// Objects that do an llSay have their messages delivered here and for nearby avatars,
296 /// the OnChatFromClient event is used.
297 /// </summary>
298 /// <param name="type">type of delvery (whisper,say,shout or regionwide)</param>
299 /// <param name="channel">channel to sent on</param>
300 /// <param name="name">name of sender (object or avatar)</param>
301 /// <param name="id">key of sender (object or avatar)</param>
302 /// <param name="msg">msg to sent</param>
303 public void DeliverMessage(ChatTypeEnum type, int channel,
304 string name, UUID id, string msg, Vector3 position)
305 {
306 // m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}",
307 // type, channel, name, id, msg);
308  
309 // Determine which listen event filters match the given set of arguments, this results
310 // in a limited set of listeners, each belonging a host. If the host is in range, add them
311 // to the pending queue.
312 foreach (ListenerInfo li
313 in m_listenerManager.GetListeners(UUID.Zero, channel,
314 name, id, msg))
315 {
316 // Dont process if this message is from yourself!
317 if (li.GetHostID().Equals(id))
318 continue;
319  
320 SceneObjectPart sPart = m_scene.GetSceneObjectPart(
321 li.GetHostID());
322 if (sPart == null)
323 continue;
324  
325 double dis = Util.GetDistanceTo(sPart.AbsolutePosition,
326 position);
327 switch (type)
328 {
329 case ChatTypeEnum.Whisper:
330 if (dis < m_whisperdistance)
331 QueueMessage(new ListenerInfo(li, name, id, msg));
332 break;
333  
334 case ChatTypeEnum.Say:
335 if (dis < m_saydistance)
336 QueueMessage(new ListenerInfo(li, name, id, msg));
337 break;
338  
339 case ChatTypeEnum.Shout:
340 if (dis < m_shoutdistance)
341 QueueMessage(new ListenerInfo(li, name, id, msg));
342 break;
343  
344 case ChatTypeEnum.Region:
345 QueueMessage(new ListenerInfo(li, name, id, msg));
346 break;
347 }
348 }
349 }
350  
351 /// <summary>
352 /// Delivers the message to a scene entity.
353 /// </summary>
354 /// <param name='target'>
355 /// Target.
356 /// </param>
357 /// <param name='channel'>
358 /// Channel.
359 /// </param>
360 /// <param name='name'>
361 /// Name.
362 /// </param>
363 /// <param name='id'>
364 /// Identifier.
365 /// </param>
366 /// <param name='msg'>
367 /// Message.
368 /// </param>
369 public void DeliverMessageTo(UUID target, int channel, Vector3 pos,
370 string name, UUID id, string msg)
371 {
372 // Is id an avatar?
373 ScenePresence sp = m_scene.GetScenePresence(target);
374  
375 if (sp != null)
376 {
377 // ignore if a child agent this is restricted to inside one
378 // region
379 if (sp.IsChildAgent)
380 return;
381  
382 // Send message to the avatar.
383 // Channel zero only goes to the avatar
384 // non zero channel messages only go to the attachments
385 if (channel == 0)
386 {
387 m_scene.SimChatToAgent(target, Utils.StringToBytes(msg),
388 pos, name, id, false);
389 }
390 else
391 {
392 List<SceneObjectGroup> attachments = sp.GetAttachments();
393 if (attachments.Count == 0)
394 return;
395  
396 // Get uuid of attachments
397 List<UUID> targets = new List<UUID>();
398 foreach (SceneObjectGroup sog in attachments)
399 {
400 if (!sog.IsDeleted)
401 targets.Add(sog.UUID);
402 }
403  
404 // Need to check each attachment
405 foreach (ListenerInfo li
406 in m_listenerManager.GetListeners(UUID.Zero,
407 channel, name, id, msg))
408 {
409 if (li.GetHostID().Equals(id))
410 continue;
411  
412 if (m_scene.GetSceneObjectPart(
413 li.GetHostID()) == null)
414 {
415 continue;
416 }
417  
418 if (targets.Contains(li.GetHostID()))
419 QueueMessage(new ListenerInfo(li, name, id, msg));
420 }
421 }
422  
423 return;
424 }
425  
426 // No avatar found so look for an object
427 foreach (ListenerInfo li
428 in m_listenerManager.GetListeners(UUID.Zero, channel,
429 name, id, msg))
430 {
431 // Dont process if this message is from yourself!
432 if (li.GetHostID().Equals(id))
433 continue;
434  
435 SceneObjectPart sPart = m_scene.GetSceneObjectPart(
436 li.GetHostID());
437 if (sPart == null)
438 continue;
439  
440 if (li.GetHostID().Equals(target))
441 {
442 QueueMessage(new ListenerInfo(li, name, id, msg));
443 break;
444 }
445 }
446  
447 return;
448 }
449  
450 protected void QueueMessage(ListenerInfo li)
451 {
452 lock (m_pending.SyncRoot)
453 {
454 m_pending.Enqueue(li);
455 }
456 }
457  
458 /// <summary>
459 /// Are there any listen events ready to be dispatched?
460 /// </summary>
461 /// <returns>boolean indication</returns>
462 public bool HasMessages()
463 {
464 return (m_pending.Count > 0);
465 }
466  
467 /// <summary>
468 /// Pop the first availlable listen event from the queue
469 /// </summary>
470 /// <returns>ListenerInfo with filter filled in</returns>
471 public IWorldCommListenerInfo GetNextMessage()
472 {
473 ListenerInfo li = null;
474  
475 lock (m_pending.SyncRoot)
476 {
477 li = (ListenerInfo)m_pending.Dequeue();
478 }
479  
480 return li;
481 }
482  
483 #endregion
484  
485 /********************************************************************
486 *
487 * Listener Stuff
488 *
489 * *****************************************************************/
490  
491 private void DeliverClientMessage(Object sender, OSChatMessage e)
492 {
493 if (null != e.Sender)
494 {
495 DeliverMessage(e.Type, e.Channel, e.Sender.Name,
496 e.Sender.AgentId, e.Message, e.Position);
497 }
498 else
499 {
500 DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero,
501 e.Message, e.Position);
502 }
503 }
504  
505 public Object[] GetSerializationData(UUID itemID)
506 {
507 return m_listenerManager.GetSerializationData(itemID);
508 }
509  
510 public void CreateFromData(uint localID, UUID itemID, UUID hostID,
511 Object[] data)
512 {
513 m_listenerManager.AddFromData(localID, itemID, hostID, data);
514 }
515 }
516  
517 public class ListenerManager
518 {
519 private Dictionary<int, List<ListenerInfo>> m_listeners =
520 new Dictionary<int, List<ListenerInfo>>();
521 private int m_maxlisteners;
522 private int m_maxhandles;
523 private int m_curlisteners;
524  
525 /// <summary>
526 /// Total number of listeners
527 /// </summary>
528 public int ListenerCount
529 {
530 get
531 {
532 lock (m_listeners)
533 return m_listeners.Count;
534 }
535 }
536  
537 public ListenerManager(int maxlisteners, int maxhandles)
538 {
539 m_maxlisteners = maxlisteners;
540 m_maxhandles = maxhandles;
541 m_curlisteners = 0;
542 }
543  
544 public int AddListener(uint localID, UUID itemID, UUID hostID,
545 int channel, string name, UUID id, string msg)
546 {
547 return AddListener(localID, itemID, hostID, channel, name, id,
548 msg, 0);
549 }
550  
551 public int AddListener(uint localID, UUID itemID, UUID hostID,
552 int channel, string name, UUID id, string msg,
553 int regexBitfield)
554 {
555 // do we already have a match on this particular filter event?
556 List<ListenerInfo> coll = GetListeners(itemID, channel, name, id,
557 msg);
558  
559 if (coll.Count > 0)
560 {
561 // special case, called with same filter settings, return same
562 // handle (2008-05-02, tested on 1.21.1 server, still holds)
563 return coll[0].GetHandle();
564 }
565  
566 if (m_curlisteners < m_maxlisteners)
567 {
568 lock (m_listeners)
569 {
570 int newHandle = GetNewHandle(itemID);
571  
572 if (newHandle > 0)
573 {
574 ListenerInfo li = new ListenerInfo(newHandle, localID,
575 itemID, hostID, channel, name, id, msg,
576 regexBitfield);
577  
578 List<ListenerInfo> listeners;
579 if (!m_listeners.TryGetValue(
580 channel, out listeners))
581 {
582 listeners = new List<ListenerInfo>();
583 m_listeners.Add(channel, listeners);
584 }
585 listeners.Add(li);
586 m_curlisteners++;
587  
588 return newHandle;
589 }
590 }
591 }
592 return -1;
593 }
594  
595 public void Remove(UUID itemID, int handle)
596 {
597 lock (m_listeners)
598 {
599 foreach (KeyValuePair<int, List<ListenerInfo>> lis
600 in m_listeners)
601 {
602 foreach (ListenerInfo li in lis.Value)
603 {
604 if (li.GetItemID().Equals(itemID) &&
605 li.GetHandle().Equals(handle))
606 {
607 lis.Value.Remove(li);
608 if (lis.Value.Count == 0)
609 {
610 m_listeners.Remove(lis.Key);
611 m_curlisteners--;
612 }
613 // there should be only one, so we bail out early
614 return;
615 }
616 }
617 }
618 }
619 }
620  
621 public void DeleteListener(UUID itemID)
622 {
623 List<int> emptyChannels = new List<int>();
624 List<ListenerInfo> removedListeners = new List<ListenerInfo>();
625  
626 lock (m_listeners)
627 {
628 foreach (KeyValuePair<int, List<ListenerInfo>> lis
629 in m_listeners)
630 {
631 foreach (ListenerInfo li in lis.Value)
632 {
633 if (li.GetItemID().Equals(itemID))
634 {
635 // store them first, else the enumerated bails on
636 // us
637 removedListeners.Add(li);
638 }
639 }
640 foreach (ListenerInfo li in removedListeners)
641 {
642 lis.Value.Remove(li);
643 m_curlisteners--;
644 }
645 removedListeners.Clear();
646 if (lis.Value.Count == 0)
647 {
648 // again, store first, remove later
649 emptyChannels.Add(lis.Key);
650 }
651 }
652 foreach (int channel in emptyChannels)
653 {
654 m_listeners.Remove(channel);
655 }
656 }
657 }
658  
659 public void Activate(UUID itemID, int handle)
660 {
661 lock (m_listeners)
662 {
663 foreach (KeyValuePair<int, List<ListenerInfo>> lis
664 in m_listeners)
665 {
666 foreach (ListenerInfo li in lis.Value)
667 {
668 if (li.GetItemID().Equals(itemID) &&
669 li.GetHandle() == handle)
670 {
671 li.Activate();
672 // only one, bail out
673 return;
674 }
675 }
676 }
677 }
678 }
679  
680 public void Dectivate(UUID itemID, int handle)
681 {
682 lock (m_listeners)
683 {
684 foreach (KeyValuePair<int, List<ListenerInfo>> lis
685 in m_listeners)
686 {
687 foreach (ListenerInfo li in lis.Value)
688 {
689 if (li.GetItemID().Equals(itemID) &&
690 li.GetHandle() == handle)
691 {
692 li.Deactivate();
693 // only one, bail out
694 return;
695 }
696 }
697 }
698 }
699 }
700  
701 /// <summary>
702 /// non-locked access, since its always called in the context of the
703 /// lock
704 /// </summary>
705 /// <param name="itemID"></param>
706 /// <returns></returns>
707 private int GetNewHandle(UUID itemID)
708 {
709 List<int> handles = new List<int>();
710  
711 // build a list of used keys for this specific itemID...
712 foreach (KeyValuePair<int, List<ListenerInfo>> lis in m_listeners)
713 {
714 foreach (ListenerInfo li in lis.Value)
715 {
716 if (li.GetItemID().Equals(itemID))
717 handles.Add(li.GetHandle());
718 }
719 }
720  
721 // Note: 0 is NOT a valid handle for llListen() to return
722 for (int i = 1; i <= m_maxhandles; i++)
723 {
724 if (!handles.Contains(i))
725 return i;
726 }
727  
728 return -1;
729 }
730  
731 /// These are duplicated from ScriptBaseClass
732 /// http://opensimulator.org/mantis/view.php?id=6106#c21945
733 #region Constants for the bitfield parameter of osListenRegex
734  
735 /// <summary>
736 /// process name parameter as regex
737 /// </summary>
738 public const int OS_LISTEN_REGEX_NAME = 0x1;
739  
740 /// <summary>
741 /// process message parameter as regex
742 /// </summary>
743 public const int OS_LISTEN_REGEX_MESSAGE = 0x2;
744  
745 #endregion
746  
747 /// <summary>
748 /// Get listeners matching the input parameters.
749 /// </summary>
750 /// <remarks>
751 /// Theres probably a more clever and efficient way to do this, maybe
752 /// with regex.
753 /// PM2008: Ha, one could even be smart and define a specialized
754 /// Enumerator.
755 /// </remarks>
756 /// <param name="itemID"></param>
757 /// <param name="channel"></param>
758 /// <param name="name"></param>
759 /// <param name="id"></param>
760 /// <param name="msg"></param>
761 /// <returns></returns>
762 public List<ListenerInfo> GetListeners(UUID itemID, int channel,
763 string name, UUID id, string msg)
764 {
765 List<ListenerInfo> collection = new List<ListenerInfo>();
766  
767 lock (m_listeners)
768 {
769 List<ListenerInfo> listeners;
770 if (!m_listeners.TryGetValue(channel, out listeners))
771 {
772 return collection;
773 }
774  
775 foreach (ListenerInfo li in listeners)
776 {
777 if (!li.IsActive())
778 {
779 continue;
780 }
781 if (!itemID.Equals(UUID.Zero) &&
782 !li.GetItemID().Equals(itemID))
783 {
784 continue;
785 }
786 if (li.GetName().Length > 0 && (
787 ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) != OS_LISTEN_REGEX_NAME && !li.GetName().Equals(name)) ||
788 ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) == OS_LISTEN_REGEX_NAME && !Regex.IsMatch(name, li.GetName()))
789 ))
790 {
791 continue;
792 }
793 if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id))
794 {
795 continue;
796 }
797 if (li.GetMessage().Length > 0 && (
798 ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) != OS_LISTEN_REGEX_MESSAGE && !li.GetMessage().Equals(msg)) ||
799 ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) == OS_LISTEN_REGEX_MESSAGE && !Regex.IsMatch(msg, li.GetMessage()))
800 ))
801 {
802 continue;
803 }
804 collection.Add(li);
805 }
806 }
807 return collection;
808 }
809  
810 public Object[] GetSerializationData(UUID itemID)
811 {
812 List<Object> data = new List<Object>();
813  
814 lock (m_listeners)
815 {
816 foreach (List<ListenerInfo> list in m_listeners.Values)
817 {
818 foreach (ListenerInfo l in list)
819 {
820 if (l.GetItemID() == itemID)
821 data.AddRange(l.GetSerializationData());
822 }
823 }
824 }
825 return (Object[])data.ToArray();
826 }
827  
828 public void AddFromData(uint localID, UUID itemID, UUID hostID,
829 Object[] data)
830 {
831 int idx = 0;
832 Object[] item = new Object[6];
833 int dataItemLength = 6;
834  
835 while (idx < data.Length)
836 {
837 dataItemLength = (idx + 7 == data.Length || (idx + 7 < data.Length && data[idx + 7] is bool)) ? 7 : 6;
838 item = new Object[dataItemLength];
839 Array.Copy(data, idx, item, 0, dataItemLength);
840  
841 ListenerInfo info =
842 ListenerInfo.FromData(localID, itemID, hostID, item);
843  
844 lock (m_listeners)
845 {
846 if (!m_listeners.ContainsKey((int)item[2]))
847 {
848 m_listeners.Add((int)item[2],
849 new List<ListenerInfo>());
850 }
851 m_listeners[(int)item[2]].Add(info);
852 }
853  
854 idx += dataItemLength;
855 }
856 }
857 }
858  
859 public class ListenerInfo : IWorldCommListenerInfo
860 {
861 /// <summary>
862 /// Listener is active or not
863 /// </summary>
864 private bool m_active;
865  
866 /// <summary>
867 /// Assigned handle of this listener
868 /// </summary>
869 private int m_handle;
870  
871 /// <summary>
872 /// Local ID from script engine
873 /// </summary>
874 private uint m_localID;
875  
876 /// <summary>
877 /// ID of the host script engine
878 /// </summary>
879 private UUID m_itemID;
880  
881 /// <summary>
882 /// ID of the host/scene part
883 /// </summary>
884 private UUID m_hostID;
885  
886 /// <summary>
887 /// Channel
888 /// </summary>
889 private int m_channel;
890  
891 /// <summary>
892 /// ID to filter messages from
893 /// </summary>
894 private UUID m_id;
895  
896 /// <summary>
897 /// Object name to filter messages from
898 /// </summary>
899 private string m_name;
900  
901 /// <summary>
902 /// The message
903 /// </summary>
904 private string m_message;
905  
906 public ListenerInfo(int handle, uint localID, UUID ItemID,
907 UUID hostID, int channel, string name, UUID id,
908 string message)
909 {
910 Initialise(handle, localID, ItemID, hostID, channel, name, id,
911 message, 0);
912 }
913  
914 public ListenerInfo(int handle, uint localID, UUID ItemID,
915 UUID hostID, int channel, string name, UUID id,
916 string message, int regexBitfield)
917 {
918 Initialise(handle, localID, ItemID, hostID, channel, name, id,
919 message, regexBitfield);
920 }
921  
922 public ListenerInfo(ListenerInfo li, string name, UUID id,
923 string message)
924 {
925 Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID,
926 li.m_channel, name, id, message, 0);
927 }
928  
929 public ListenerInfo(ListenerInfo li, string name, UUID id,
930 string message, int regexBitfield)
931 {
932 Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID,
933 li.m_channel, name, id, message, regexBitfield);
934 }
935  
936 private void Initialise(int handle, uint localID, UUID ItemID,
937 UUID hostID, int channel, string name, UUID id,
938 string message, int regexBitfield)
939 {
940 m_active = true;
941 m_handle = handle;
942 m_localID = localID;
943 m_itemID = ItemID;
944 m_hostID = hostID;
945 m_channel = channel;
946 m_name = name;
947 m_id = id;
948 m_message = message;
949 RegexBitfield = regexBitfield;
950 }
951  
952 public Object[] GetSerializationData()
953 {
954 Object[] data = new Object[7];
955  
956 data[0] = m_active;
957 data[1] = m_handle;
958 data[2] = m_channel;
959 data[3] = m_name;
960 data[4] = m_id;
961 data[5] = m_message;
962 data[6] = RegexBitfield;
963  
964 return data;
965 }
966  
967 public static ListenerInfo FromData(uint localID, UUID ItemID,
968 UUID hostID, Object[] data)
969 {
970 ListenerInfo linfo = new ListenerInfo((int)data[1], localID,
971 ItemID, hostID, (int)data[2], (string)data[3],
972 (UUID)data[4], (string)data[5]);
973 linfo.m_active = (bool)data[0];
974 if (data.Length >= 7)
975 {
976 linfo.RegexBitfield = (int)data[6];
977 }
978  
979 return linfo;
980 }
981  
982 public UUID GetItemID()
983 {
984 return m_itemID;
985 }
986  
987 public UUID GetHostID()
988 {
989 return m_hostID;
990 }
991  
992 public int GetChannel()
993 {
994 return m_channel;
995 }
996  
997 public uint GetLocalID()
998 {
999 return m_localID;
1000 }
1001  
1002 public int GetHandle()
1003 {
1004 return m_handle;
1005 }
1006  
1007 public string GetMessage()
1008 {
1009 return m_message;
1010 }
1011  
1012 public string GetName()
1013 {
1014 return m_name;
1015 }
1016  
1017 public bool IsActive()
1018 {
1019 return m_active;
1020 }
1021  
1022 public void Deactivate()
1023 {
1024 m_active = false;
1025 }
1026  
1027 public void Activate()
1028 {
1029 m_active = true;
1030 }
1031  
1032 public UUID GetID()
1033 {
1034 return m_id;
1035 }
1036  
1037 public int RegexBitfield { get; private set; }
1038 }
1039 }