WorldCommModule.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  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 const int DEBUG_CHANNEL = 2147483647;
  91. private ListenerManager m_listenerManager;
  92. private Queue m_pending;
  93. private Queue m_pendingQ;
  94. private Scene m_scene;
  95. private int m_whisperdistance = 10;
  96. private int m_saydistance = 20;
  97. private int m_shoutdistance = 100;
  98. #region INonSharedRegionModule Members
  99. public void Initialise(IConfigSource config)
  100. {
  101. // wrap this in a try block so that defaults will work if
  102. // the config file doesn't specify otherwise.
  103. int maxlisteners = 1000;
  104. int maxhandles = 65;
  105. try
  106. {
  107. m_whisperdistance = config.Configs["Chat"].GetInt(
  108. "whisper_distance", m_whisperdistance);
  109. m_saydistance = config.Configs["Chat"].GetInt(
  110. "say_distance", m_saydistance);
  111. m_shoutdistance = config.Configs["Chat"].GetInt(
  112. "shout_distance", m_shoutdistance);
  113. maxlisteners = config.Configs["LL-Functions"].GetInt(
  114. "max_listens_per_region", maxlisteners);
  115. maxhandles = config.Configs["LL-Functions"].GetInt(
  116. "max_listens_per_script", maxhandles);
  117. }
  118. catch (Exception)
  119. {
  120. }
  121. if (maxlisteners < 1)
  122. maxlisteners = int.MaxValue;
  123. if (maxhandles < 1)
  124. maxhandles = int.MaxValue;
  125. if (maxlisteners < maxhandles)
  126. maxlisteners = maxhandles;
  127. m_listenerManager = new ListenerManager(maxlisteners, maxhandles);
  128. m_pendingQ = new Queue();
  129. m_pending = Queue.Synchronized(m_pendingQ);
  130. }
  131. public void PostInitialise()
  132. {
  133. }
  134. public void AddRegion(Scene scene)
  135. {
  136. m_scene = scene;
  137. m_scene.RegisterModuleInterface<IWorldComm>(this);
  138. m_scene.EventManager.OnChatFromClient += DeliverClientMessage;
  139. m_scene.EventManager.OnChatBroadcast += DeliverClientMessage;
  140. }
  141. public void RegionLoaded(Scene scene) { }
  142. public void RemoveRegion(Scene scene)
  143. {
  144. if (scene != m_scene)
  145. return;
  146. m_scene.UnregisterModuleInterface<IWorldComm>(this);
  147. m_scene.EventManager.OnChatBroadcast -= DeliverClientMessage;
  148. m_scene.EventManager.OnChatBroadcast -= DeliverClientMessage;
  149. }
  150. public void Close()
  151. {
  152. }
  153. public string Name
  154. {
  155. get { return "WorldCommModule"; }
  156. }
  157. public Type ReplaceableInterface { get { return null; } }
  158. #endregion
  159. #region IWorldComm Members
  160. public int ListenerCount
  161. {
  162. get
  163. {
  164. return m_listenerManager.ListenerCount;
  165. }
  166. }
  167. /// <summary>
  168. /// Create a listen event callback with the specified filters.
  169. /// The parameters localID,itemID are needed to uniquely identify
  170. /// the script during 'peek' time. Parameter hostID is needed to
  171. /// determine the position of the script.
  172. /// </summary>
  173. /// <param name="localID">localID of the script engine</param>
  174. /// <param name="itemID">UUID of the script engine</param>
  175. /// <param name="hostID">UUID of the SceneObjectPart</param>
  176. /// <param name="channel">channel to listen on</param>
  177. /// <param name="name">name to filter on</param>
  178. /// <param name="id">
  179. /// key to filter on (user given, could be totally faked)
  180. /// </param>
  181. /// <param name="msg">msg to filter on</param>
  182. /// <returns>number of the scripts handle</returns>
  183. public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
  184. string name, UUID id, string msg)
  185. {
  186. return m_listenerManager.AddListener(localID, itemID, hostID,
  187. channel, name, id, msg);
  188. }
  189. /// <summary>
  190. /// Create a listen event callback with the specified filters.
  191. /// The parameters localID,itemID are needed to uniquely identify
  192. /// the script during 'peek' time. Parameter hostID is needed to
  193. /// determine the position of the script.
  194. /// </summary>
  195. /// <param name="localID">localID of the script engine</param>
  196. /// <param name="itemID">UUID of the script engine</param>
  197. /// <param name="hostID">UUID of the SceneObjectPart</param>
  198. /// <param name="channel">channel to listen on</param>
  199. /// <param name="name">name to filter on</param>
  200. /// <param name="id">
  201. /// key to filter on (user given, could be totally faked)
  202. /// </param>
  203. /// <param name="msg">msg to filter on</param>
  204. /// <param name="regexBitfield">
  205. /// Bitfield indicating which strings should be processed as regex.
  206. /// </param>
  207. /// <returns>number of the scripts handle</returns>
  208. public int Listen(uint localID, UUID itemID, UUID hostID, int channel,
  209. string name, UUID id, string msg, int regexBitfield)
  210. {
  211. return m_listenerManager.AddListener(localID, itemID, hostID,
  212. channel, name, id, msg, regexBitfield);
  213. }
  214. /// <summary>
  215. /// Sets the listen event with handle as active (active = TRUE) or inactive (active = FALSE).
  216. /// The handle used is returned from Listen()
  217. /// </summary>
  218. /// <param name="itemID">UUID of the script engine</param>
  219. /// <param name="handle">handle returned by Listen()</param>
  220. /// <param name="active">temp. activate or deactivate the Listen()</param>
  221. public void ListenControl(UUID itemID, int handle, int active)
  222. {
  223. if (active == 1)
  224. m_listenerManager.Activate(itemID, handle);
  225. else if (active == 0)
  226. m_listenerManager.Dectivate(itemID, handle);
  227. }
  228. /// <summary>
  229. /// Removes the listen event callback with handle
  230. /// </summary>
  231. /// <param name="itemID">UUID of the script engine</param>
  232. /// <param name="handle">handle returned by Listen()</param>
  233. public void ListenRemove(UUID itemID, int handle)
  234. {
  235. m_listenerManager.Remove(itemID, handle);
  236. }
  237. /// <summary>
  238. /// Removes all listen event callbacks for the given itemID
  239. /// (script engine)
  240. /// </summary>
  241. /// <param name="itemID">UUID of the script engine</param>
  242. public void DeleteListener(UUID itemID)
  243. {
  244. m_listenerManager.DeleteListener(itemID);
  245. }
  246. protected static Vector3 CenterOfRegion = new Vector3(128, 128, 20);
  247. public void DeliverMessage(ChatTypeEnum type, int channel, string name, UUID id, string msg)
  248. {
  249. Vector3 position;
  250. SceneObjectPart source;
  251. ScenePresence avatar;
  252. if ((source = m_scene.GetSceneObjectPart(id)) != null)
  253. position = source.AbsolutePosition;
  254. else if ((avatar = m_scene.GetScenePresence(id)) != null)
  255. position = avatar.AbsolutePosition;
  256. else if (ChatTypeEnum.Region == type)
  257. position = CenterOfRegion;
  258. else
  259. return;
  260. DeliverMessage(type, channel, name, id, msg, position);
  261. }
  262. /// <summary>
  263. /// This method scans over the objects which registered an interest in listen callbacks.
  264. /// For everyone it finds, it checks if it fits the given filter. If it does, then
  265. /// enqueue the message for delivery to the objects listen event handler.
  266. /// The enqueued ListenerInfo no longer has filter values, but the actually trigged values.
  267. /// Objects that do an llSay have their messages delivered here and for nearby avatars,
  268. /// the OnChatFromClient event is used.
  269. /// </summary>
  270. /// <param name="type">type of delvery (whisper,say,shout or regionwide)</param>
  271. /// <param name="channel">channel to sent on</param>
  272. /// <param name="name">name of sender (object or avatar)</param>
  273. /// <param name="id">key of sender (object or avatar)</param>
  274. /// <param name="msg">msg to sent</param>
  275. public void DeliverMessage(ChatTypeEnum type, int channel,
  276. string name, UUID id, string msg, Vector3 position)
  277. {
  278. // m_log.DebugFormat("[WorldComm] got[2] type {0}, channel {1}, name {2}, id {3}, msg {4}",
  279. // type, channel, name, id, msg);
  280. // Determine which listen event filters match the given set of arguments, this results
  281. // in a limited set of listeners, each belonging a host. If the host is in range, add them
  282. // to the pending queue.
  283. foreach (ListenerInfo li
  284. in m_listenerManager.GetListeners(UUID.Zero, channel,
  285. name, id, msg))
  286. {
  287. // Dont process if this message is from yourself!
  288. if (li.GetHostID().Equals(id))
  289. continue;
  290. SceneObjectPart sPart = m_scene.GetSceneObjectPart(
  291. li.GetHostID());
  292. if (sPart == null)
  293. continue;
  294. double dis = Util.GetDistanceTo(sPart.AbsolutePosition,
  295. position);
  296. switch (type)
  297. {
  298. case ChatTypeEnum.Whisper:
  299. if (dis < m_whisperdistance)
  300. QueueMessage(new ListenerInfo(li, name, id, msg));
  301. break;
  302. case ChatTypeEnum.Say:
  303. if (dis < m_saydistance)
  304. QueueMessage(new ListenerInfo(li, name, id, msg));
  305. break;
  306. case ChatTypeEnum.Shout:
  307. if (dis < m_shoutdistance)
  308. QueueMessage(new ListenerInfo(li, name, id, msg));
  309. break;
  310. case ChatTypeEnum.Region:
  311. QueueMessage(new ListenerInfo(li, name, id, msg));
  312. break;
  313. }
  314. }
  315. }
  316. /// <summary>
  317. /// Delivers the message to a scene entity.
  318. /// </summary>
  319. /// <param name='target'>
  320. /// Target.
  321. /// </param>
  322. /// <param name='channel'>
  323. /// Channel.
  324. /// </param>
  325. /// <param name='name'>
  326. /// Name.
  327. /// </param>
  328. /// <param name='id'>
  329. /// Identifier.
  330. /// </param>
  331. /// <param name='msg'>
  332. /// Message.
  333. /// </param>
  334. public void DeliverMessageTo(UUID target, int channel, Vector3 pos, string name, UUID id, string msg)
  335. {
  336. if (channel == DEBUG_CHANNEL)
  337. return;
  338. if(target == UUID.Zero)
  339. return;
  340. // Is target an avatar?
  341. ScenePresence sp = m_scene.GetScenePresence(target);
  342. if (sp != null)
  343. {
  344. // Send message to avatar
  345. if (channel == 0)
  346. {
  347. // Channel 0 goes to viewer ONLY
  348. m_scene.SimChat(Utils.StringToBytes(msg), ChatTypeEnum.Broadcast, 0, pos, name, id, target, false, false);
  349. return;
  350. }
  351. // for now messages to prims don't cross regions
  352. if(sp.IsChildAgent)
  353. return;
  354. List<SceneObjectGroup> attachments = sp.GetAttachments();
  355. if (attachments.Count == 0)
  356. return;
  357. // Get uuid of attachments
  358. List<UUID> targets = new List<UUID>();
  359. foreach (SceneObjectGroup sog in attachments)
  360. {
  361. if (!sog.IsDeleted)
  362. {
  363. SceneObjectPart[] parts = sog.Parts;
  364. foreach(SceneObjectPart p in parts)
  365. targets.Add(p.UUID);
  366. }
  367. }
  368. foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg))
  369. {
  370. UUID liHostID = li.GetHostID();
  371. if (liHostID.Equals(id))
  372. continue;
  373. if (m_scene.GetSceneObjectPart(liHostID) == null)
  374. continue;
  375. if (targets.Contains(liHostID))
  376. QueueMessage(new ListenerInfo(li, name, id, msg));
  377. }
  378. return;
  379. }
  380. SceneObjectPart part = m_scene.GetSceneObjectPart(target);
  381. if (part == null) // Not even an object
  382. return; // No error
  383. foreach (ListenerInfo li in m_listenerManager.GetListeners(UUID.Zero, channel, name, id, msg))
  384. {
  385. UUID liHostID = li.GetHostID();
  386. // Dont process if this message is from yourself!
  387. if (liHostID.Equals(id))
  388. continue;
  389. if (m_scene.GetSceneObjectPart(liHostID) == null)
  390. continue;
  391. if (liHostID.Equals(target))
  392. {
  393. QueueMessage(new ListenerInfo(li, name, id, msg));
  394. break;
  395. }
  396. }
  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. lock (m_listeners)
  499. {
  500. if (m_curlisteners < m_maxlisteners)
  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. m_curlisteners--;
  537. if (lis.Value.Count == 0)
  538. m_listeners.Remove(lis.Key); // bailing of loop so this does not smoke
  539. // there should be only one, so we bail out early
  540. return;
  541. }
  542. }
  543. }
  544. }
  545. }
  546. public void DeleteListener(UUID itemID)
  547. {
  548. List<int> emptyChannels = new List<int>();
  549. List<ListenerInfo> removedListeners = new List<ListenerInfo>();
  550. lock (m_listeners)
  551. {
  552. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  553. in m_listeners)
  554. {
  555. foreach (ListenerInfo li in lis.Value)
  556. {
  557. if (li.GetItemID().Equals(itemID))
  558. {
  559. // store them first, else the enumerated bails on
  560. // us
  561. removedListeners.Add(li);
  562. }
  563. }
  564. foreach (ListenerInfo li in removedListeners)
  565. {
  566. lis.Value.Remove(li);
  567. m_curlisteners--;
  568. }
  569. removedListeners.Clear();
  570. if (lis.Value.Count == 0)
  571. {
  572. // again, store first, remove later
  573. emptyChannels.Add(lis.Key);
  574. }
  575. }
  576. foreach (int channel in emptyChannels)
  577. {
  578. m_listeners.Remove(channel);
  579. }
  580. }
  581. }
  582. public void Activate(UUID itemID, int handle)
  583. {
  584. lock (m_listeners)
  585. {
  586. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  587. in m_listeners)
  588. {
  589. foreach (ListenerInfo li in lis.Value)
  590. {
  591. if (li.GetItemID().Equals(itemID) &&
  592. li.GetHandle() == handle)
  593. {
  594. li.Activate();
  595. // only one, bail out
  596. return;
  597. }
  598. }
  599. }
  600. }
  601. }
  602. public void Dectivate(UUID itemID, int handle)
  603. {
  604. lock (m_listeners)
  605. {
  606. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  607. in m_listeners)
  608. {
  609. foreach (ListenerInfo li in lis.Value)
  610. {
  611. if (li.GetItemID().Equals(itemID) &&
  612. li.GetHandle() == handle)
  613. {
  614. li.Deactivate();
  615. // only one, bail out
  616. return;
  617. }
  618. }
  619. }
  620. }
  621. }
  622. /// <summary>
  623. /// non-locked access, since its always called in the context of the
  624. /// lock
  625. /// </summary>
  626. /// <param name="itemID"></param>
  627. /// <returns></returns>
  628. private int GetNewHandle(UUID itemID)
  629. {
  630. List<int> handles = new List<int>();
  631. // build a list of used keys for this specific itemID...
  632. foreach (KeyValuePair<int, List<ListenerInfo>> lis in m_listeners)
  633. {
  634. foreach (ListenerInfo li in lis.Value)
  635. {
  636. if (li.GetItemID().Equals(itemID))
  637. handles.Add(li.GetHandle());
  638. }
  639. }
  640. if(handles.Count >= m_maxhandles)
  641. return -1;
  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. }