PageRenderTime 61ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs

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