WorldCommModule.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  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.Direct, 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 (!liHostID.Equals(target))
  390. continue;
  391. if (m_scene.GetSceneObjectPart(liHostID) == null)
  392. continue;
  393. QueueMessage(new ListenerInfo(li, name, id, msg));
  394. }
  395. }
  396. protected void QueueMessage(ListenerInfo li)
  397. {
  398. lock (m_pending.SyncRoot)
  399. {
  400. m_pending.Enqueue(li);
  401. }
  402. }
  403. /// <summary>
  404. /// Are there any listen events ready to be dispatched?
  405. /// </summary>
  406. /// <returns>boolean indication</returns>
  407. public bool HasMessages()
  408. {
  409. return (m_pending.Count > 0);
  410. }
  411. /// <summary>
  412. /// Pop the first availlable listen event from the queue
  413. /// </summary>
  414. /// <returns>ListenerInfo with filter filled in</returns>
  415. public IWorldCommListenerInfo GetNextMessage()
  416. {
  417. ListenerInfo li = null;
  418. lock (m_pending.SyncRoot)
  419. {
  420. li = (ListenerInfo)m_pending.Dequeue();
  421. }
  422. return li;
  423. }
  424. #endregion
  425. /********************************************************************
  426. *
  427. * Listener Stuff
  428. *
  429. * *****************************************************************/
  430. private void DeliverClientMessage(Object sender, OSChatMessage e)
  431. {
  432. if (null != e.Sender)
  433. {
  434. DeliverMessage(e.Type, e.Channel, e.Sender.Name,
  435. e.Sender.AgentId, e.Message, e.Position);
  436. }
  437. else
  438. {
  439. DeliverMessage(e.Type, e.Channel, e.From, UUID.Zero,
  440. e.Message, e.Position);
  441. }
  442. }
  443. public Object[] GetSerializationData(UUID itemID)
  444. {
  445. return m_listenerManager.GetSerializationData(itemID);
  446. }
  447. public void CreateFromData(uint localID, UUID itemID, UUID hostID,
  448. Object[] data)
  449. {
  450. m_listenerManager.AddFromData(localID, itemID, hostID, data);
  451. }
  452. }
  453. public class ListenerManager
  454. {
  455. private Dictionary<int, List<ListenerInfo>> m_listeners =
  456. new Dictionary<int, List<ListenerInfo>>();
  457. private int m_maxlisteners;
  458. private int m_maxhandles;
  459. private int m_curlisteners;
  460. /// <summary>
  461. /// Total number of listeners
  462. /// </summary>
  463. public int ListenerCount
  464. {
  465. get
  466. {
  467. lock (m_listeners)
  468. return m_listeners.Count;
  469. }
  470. }
  471. public ListenerManager(int maxlisteners, int maxhandles)
  472. {
  473. m_maxlisteners = maxlisteners;
  474. m_maxhandles = maxhandles;
  475. m_curlisteners = 0;
  476. }
  477. public int AddListener(uint localID, UUID itemID, UUID hostID,
  478. int channel, string name, UUID id, string msg)
  479. {
  480. return AddListener(localID, itemID, hostID, channel, name, id,
  481. msg, 0);
  482. }
  483. public int AddListener(uint localID, UUID itemID, UUID hostID,
  484. int channel, string name, UUID id, string msg,
  485. int regexBitfield)
  486. {
  487. // do we already have a match on this particular filter event?
  488. List<ListenerInfo> coll = GetListeners(itemID, channel, name, id,
  489. msg);
  490. if (coll.Count > 0)
  491. {
  492. // special case, called with same filter settings, return same
  493. // handle (2008-05-02, tested on 1.21.1 server, still holds)
  494. return coll[0].GetHandle();
  495. }
  496. lock (m_listeners)
  497. {
  498. if (m_curlisteners < m_maxlisteners)
  499. {
  500. int newHandle = GetNewHandle(itemID);
  501. if (newHandle > 0)
  502. {
  503. ListenerInfo li = new ListenerInfo(newHandle, localID,
  504. itemID, hostID, channel, name, id, msg,
  505. regexBitfield);
  506. List<ListenerInfo> listeners;
  507. if (!m_listeners.TryGetValue(
  508. channel, out listeners))
  509. {
  510. listeners = new List<ListenerInfo>();
  511. m_listeners.Add(channel, listeners);
  512. }
  513. listeners.Add(li);
  514. m_curlisteners++;
  515. return newHandle;
  516. }
  517. }
  518. }
  519. return -1;
  520. }
  521. public void Remove(UUID itemID, int handle)
  522. {
  523. lock (m_listeners)
  524. {
  525. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  526. in m_listeners)
  527. {
  528. foreach (ListenerInfo li in lis.Value)
  529. {
  530. if (li.GetItemID().Equals(itemID) &&
  531. li.GetHandle().Equals(handle))
  532. {
  533. lis.Value.Remove(li);
  534. m_curlisteners--;
  535. if (lis.Value.Count == 0)
  536. m_listeners.Remove(lis.Key); // bailing of loop so this does not smoke
  537. // there should be only one, so we bail out early
  538. return;
  539. }
  540. }
  541. }
  542. }
  543. }
  544. public void DeleteListener(UUID itemID)
  545. {
  546. List<int> emptyChannels = new List<int>();
  547. List<ListenerInfo> removedListeners = new List<ListenerInfo>();
  548. lock (m_listeners)
  549. {
  550. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  551. in m_listeners)
  552. {
  553. foreach (ListenerInfo li in lis.Value)
  554. {
  555. if (li.GetItemID().Equals(itemID))
  556. {
  557. // store them first, else the enumerated bails on
  558. // us
  559. removedListeners.Add(li);
  560. }
  561. }
  562. foreach (ListenerInfo li in removedListeners)
  563. {
  564. lis.Value.Remove(li);
  565. m_curlisteners--;
  566. }
  567. removedListeners.Clear();
  568. if (lis.Value.Count == 0)
  569. {
  570. // again, store first, remove later
  571. emptyChannels.Add(lis.Key);
  572. }
  573. }
  574. foreach (int channel in emptyChannels)
  575. {
  576. m_listeners.Remove(channel);
  577. }
  578. }
  579. }
  580. public void Activate(UUID itemID, int handle)
  581. {
  582. lock (m_listeners)
  583. {
  584. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  585. in m_listeners)
  586. {
  587. foreach (ListenerInfo li in lis.Value)
  588. {
  589. if (li.GetItemID().Equals(itemID) &&
  590. li.GetHandle() == handle)
  591. {
  592. li.Activate();
  593. // only one, bail out
  594. return;
  595. }
  596. }
  597. }
  598. }
  599. }
  600. public void Dectivate(UUID itemID, int handle)
  601. {
  602. lock (m_listeners)
  603. {
  604. foreach (KeyValuePair<int, List<ListenerInfo>> lis
  605. in m_listeners)
  606. {
  607. foreach (ListenerInfo li in lis.Value)
  608. {
  609. if (li.GetItemID().Equals(itemID) &&
  610. li.GetHandle() == handle)
  611. {
  612. li.Deactivate();
  613. // only one, bail out
  614. return;
  615. }
  616. }
  617. }
  618. }
  619. }
  620. /// <summary>
  621. /// non-locked access, since its always called in the context of the
  622. /// lock
  623. /// </summary>
  624. /// <param name="itemID"></param>
  625. /// <returns></returns>
  626. private int GetNewHandle(UUID itemID)
  627. {
  628. List<int> handles = new List<int>();
  629. // build a list of used keys for this specific itemID...
  630. foreach (KeyValuePair<int, List<ListenerInfo>> lis in m_listeners)
  631. {
  632. foreach (ListenerInfo li in lis.Value)
  633. {
  634. if (li.GetItemID().Equals(itemID))
  635. handles.Add(li.GetHandle());
  636. }
  637. }
  638. if(handles.Count >= m_maxhandles)
  639. return -1;
  640. // Note: 0 is NOT a valid handle for llListen() to return
  641. for (int i = 1; i <= m_maxhandles; i++)
  642. {
  643. if (!handles.Contains(i))
  644. return i;
  645. }
  646. return -1;
  647. }
  648. /// These are duplicated from ScriptBaseClass
  649. /// http://opensimulator.org/mantis/view.php?id=6106#c21945
  650. #region Constants for the bitfield parameter of osListenRegex
  651. /// <summary>
  652. /// process name parameter as regex
  653. /// </summary>
  654. public const int OS_LISTEN_REGEX_NAME = 0x1;
  655. /// <summary>
  656. /// process message parameter as regex
  657. /// </summary>
  658. public const int OS_LISTEN_REGEX_MESSAGE = 0x2;
  659. #endregion
  660. /// <summary>
  661. /// Get listeners matching the input parameters.
  662. /// </summary>
  663. /// <remarks>
  664. /// Theres probably a more clever and efficient way to do this, maybe
  665. /// with regex.
  666. /// PM2008: Ha, one could even be smart and define a specialized
  667. /// Enumerator.
  668. /// </remarks>
  669. /// <param name="itemID"></param>
  670. /// <param name="channel"></param>
  671. /// <param name="name"></param>
  672. /// <param name="id"></param>
  673. /// <param name="msg"></param>
  674. /// <returns></returns>
  675. public List<ListenerInfo> GetListeners(UUID itemID, int channel,
  676. string name, UUID id, string msg)
  677. {
  678. List<ListenerInfo> collection = new List<ListenerInfo>();
  679. lock (m_listeners)
  680. {
  681. List<ListenerInfo> listeners;
  682. if (!m_listeners.TryGetValue(channel, out listeners))
  683. {
  684. return collection;
  685. }
  686. foreach (ListenerInfo li in listeners)
  687. {
  688. if (!li.IsActive())
  689. {
  690. continue;
  691. }
  692. if (!itemID.Equals(UUID.Zero) &&
  693. !li.GetItemID().Equals(itemID))
  694. {
  695. continue;
  696. }
  697. if (li.GetName().Length > 0 && (
  698. ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) != OS_LISTEN_REGEX_NAME && !li.GetName().Equals(name)) ||
  699. ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) == OS_LISTEN_REGEX_NAME && !Regex.IsMatch(name, li.GetName()))
  700. ))
  701. {
  702. continue;
  703. }
  704. if (!li.GetID().Equals(UUID.Zero) && !li.GetID().Equals(id))
  705. {
  706. continue;
  707. }
  708. if (li.GetMessage().Length > 0 && (
  709. ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) != OS_LISTEN_REGEX_MESSAGE && !li.GetMessage().Equals(msg)) ||
  710. ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) == OS_LISTEN_REGEX_MESSAGE && !Regex.IsMatch(msg, li.GetMessage()))
  711. ))
  712. {
  713. continue;
  714. }
  715. collection.Add(li);
  716. }
  717. }
  718. return collection;
  719. }
  720. public Object[] GetSerializationData(UUID itemID)
  721. {
  722. List<Object> data = new List<Object>();
  723. lock (m_listeners)
  724. {
  725. foreach (List<ListenerInfo> list in m_listeners.Values)
  726. {
  727. foreach (ListenerInfo l in list)
  728. {
  729. if (l.GetItemID() == itemID)
  730. data.AddRange(l.GetSerializationData());
  731. }
  732. }
  733. }
  734. return (Object[])data.ToArray();
  735. }
  736. public void AddFromData(uint localID, UUID itemID, UUID hostID,
  737. Object[] data)
  738. {
  739. int idx = 0;
  740. Object[] item = new Object[6];
  741. int dataItemLength = 6;
  742. while (idx < data.Length)
  743. {
  744. dataItemLength = (idx + 7 == data.Length || (idx + 7 < data.Length && data[idx + 7] is bool)) ? 7 : 6;
  745. item = new Object[dataItemLength];
  746. Array.Copy(data, idx, item, 0, dataItemLength);
  747. ListenerInfo info =
  748. ListenerInfo.FromData(localID, itemID, hostID, item);
  749. lock (m_listeners)
  750. {
  751. if (!m_listeners.ContainsKey((int)item[2]))
  752. {
  753. m_listeners.Add((int)item[2],
  754. new List<ListenerInfo>());
  755. }
  756. m_listeners[(int)item[2]].Add(info);
  757. }
  758. idx += dataItemLength;
  759. }
  760. }
  761. }
  762. public class ListenerInfo : IWorldCommListenerInfo
  763. {
  764. /// <summary>
  765. /// Listener is active or not
  766. /// </summary>
  767. private bool m_active;
  768. /// <summary>
  769. /// Assigned handle of this listener
  770. /// </summary>
  771. private int m_handle;
  772. /// <summary>
  773. /// Local ID from script engine
  774. /// </summary>
  775. private uint m_localID;
  776. /// <summary>
  777. /// ID of the host script engine
  778. /// </summary>
  779. private UUID m_itemID;
  780. /// <summary>
  781. /// ID of the host/scene part
  782. /// </summary>
  783. private UUID m_hostID;
  784. /// <summary>
  785. /// Channel
  786. /// </summary>
  787. private int m_channel;
  788. /// <summary>
  789. /// ID to filter messages from
  790. /// </summary>
  791. private UUID m_id;
  792. /// <summary>
  793. /// Object name to filter messages from
  794. /// </summary>
  795. private string m_name;
  796. /// <summary>
  797. /// The message
  798. /// </summary>
  799. private string m_message;
  800. public ListenerInfo(int handle, uint localID, UUID ItemID,
  801. UUID hostID, int channel, string name, UUID id,
  802. string message)
  803. {
  804. Initialise(handle, localID, ItemID, hostID, channel, name, id,
  805. message, 0);
  806. }
  807. public ListenerInfo(int handle, uint localID, UUID ItemID,
  808. UUID hostID, int channel, string name, UUID id,
  809. string message, int regexBitfield)
  810. {
  811. Initialise(handle, localID, ItemID, hostID, channel, name, id,
  812. message, regexBitfield);
  813. }
  814. public ListenerInfo(ListenerInfo li, string name, UUID id,
  815. string message)
  816. {
  817. Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID,
  818. li.m_channel, name, id, message, 0);
  819. }
  820. public ListenerInfo(ListenerInfo li, string name, UUID id,
  821. string message, int regexBitfield)
  822. {
  823. Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID,
  824. li.m_channel, name, id, message, regexBitfield);
  825. }
  826. private void Initialise(int handle, uint localID, UUID ItemID,
  827. UUID hostID, int channel, string name, UUID id,
  828. string message, int regexBitfield)
  829. {
  830. m_active = true;
  831. m_handle = handle;
  832. m_localID = localID;
  833. m_itemID = ItemID;
  834. m_hostID = hostID;
  835. m_channel = channel;
  836. m_name = name;
  837. m_id = id;
  838. m_message = message;
  839. RegexBitfield = regexBitfield;
  840. }
  841. public Object[] GetSerializationData()
  842. {
  843. Object[] data = new Object[7];
  844. data[0] = m_active;
  845. data[1] = m_handle;
  846. data[2] = m_channel;
  847. data[3] = m_name;
  848. data[4] = m_id;
  849. data[5] = m_message;
  850. data[6] = RegexBitfield;
  851. return data;
  852. }
  853. public static ListenerInfo FromData(uint localID, UUID ItemID,
  854. UUID hostID, Object[] data)
  855. {
  856. ListenerInfo linfo = new ListenerInfo((int)data[1], localID,
  857. ItemID, hostID, (int)data[2], (string)data[3],
  858. (UUID)data[4], (string)data[5]);
  859. linfo.m_active = (bool)data[0];
  860. if (data.Length >= 7)
  861. {
  862. linfo.RegexBitfield = (int)data[6];
  863. }
  864. return linfo;
  865. }
  866. public UUID GetItemID()
  867. {
  868. return m_itemID;
  869. }
  870. public UUID GetHostID()
  871. {
  872. return m_hostID;
  873. }
  874. public int GetChannel()
  875. {
  876. return m_channel;
  877. }
  878. public uint GetLocalID()
  879. {
  880. return m_localID;
  881. }
  882. public int GetHandle()
  883. {
  884. return m_handle;
  885. }
  886. public string GetMessage()
  887. {
  888. return m_message;
  889. }
  890. public string GetName()
  891. {
  892. return m_name;
  893. }
  894. public bool IsActive()
  895. {
  896. return m_active;
  897. }
  898. public void Deactivate()
  899. {
  900. m_active = false;
  901. }
  902. public void Activate()
  903. {
  904. m_active = true;
  905. }
  906. public UUID GetID()
  907. {
  908. return m_id;
  909. }
  910. public int RegexBitfield { get; private set; }
  911. }
  912. }