aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Scripting/WorldComm
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Scripting/WorldComm')
-rw-r--r--OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs578
1 files changed, 578 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs
new file mode 100644
index 0000000..a949fb6
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/Scripting/WorldComm/WorldCommModule.cs
@@ -0,0 +1,578 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using libsecondlife;
31using Nini.Config;
32using OpenSim.Framework;
33using OpenSim.Region.Environment.Interfaces;
34using OpenSim.Region.Environment.Scenes;
35
36/*****************************************************
37 *
38 * WorldCommModule
39 *
40 *
41 * Holding place for world comms - basically llListen
42 * function implementation.
43 *
44 * lLListen(integer channel, string name, key id, string msg)
45 * The name, id, and msg arguments specify the filtering
46 * criteria. You can pass the empty string
47 * (or NULL_KEY for id) for these to set a completely
48 * open filter; this causes the listen() event handler to be
49 * invoked for all chat on the channel. To listen only
50 * for chat spoken by a specific object or avatar,
51 * specify the name and/or id arguments. To listen
52 * only for a specific command, specify the
53 * (case-sensitive) msg argument. If msg is not empty,
54 * listener will only hear strings which are exactly equal
55 * to msg. You can also use all the arguments to establish
56 * the most restrictive filtering criteria.
57 *
58 * It might be useful for each listener to maintain a message
59 * digest, with a list of recent messages by UUID. This can
60 * be used to prevent in-world repeater loops. However, the
61 * linden functions do not have this capability, so for now
62 * thats the way it works.
63 *
64 * **************************************************/
65
66namespace OpenSim.Region.Environment.Modules.Scripting.WorldComm
67{
68 public class WorldCommModule : IRegionModule, IWorldComm
69 {
70 private Scene m_scene;
71 private object CommListLock = new object();
72 private object ListLock = new object();
73 private string m_name = "WorldCommModule";
74 private ListenerManager m_listenerManager;
75 private Queue m_pendingQ;
76 private Queue m_pending;
77
78 public WorldCommModule()
79 {
80 }
81
82 public void Initialise(Scene scene, IConfigSource config)
83 {
84 m_scene = scene;
85 m_scene.RegisterModuleInterface<IWorldComm>(this);
86 m_listenerManager = new ListenerManager();
87 m_scene.EventManager.OnNewClient += NewClient;
88 m_pendingQ = new Queue();
89 m_pending = Queue.Synchronized(m_pendingQ);
90 }
91
92 public void PostInitialise()
93 {
94 }
95
96 public void Close()
97 {
98 }
99
100 public string Name
101 {
102 get { return m_name; }
103 }
104
105 public bool IsSharedModule
106 {
107 get { return false; }
108 }
109
110 public void NewClient(IClientAPI client)
111 {
112 client.OnChatFromViewer += DeliverClientMessage;
113 }
114
115 /********************************************************************
116 *
117 * Listener Stuff
118 *
119 * *****************************************************************/
120 private void DeliverClientMessage(Object sender, ChatFromViewerArgs e)
121 {
122 DeliverMessage(e.Sender.AgentId.ToString(),
123 e.Type, e.Channel,
124 e.Sender.FirstName + " " + e.Sender.LastName,
125 e.Message);
126 }
127
128 public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg)
129 {
130 return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg);
131 }
132
133 public void ListenControl(int handle, int active)
134 {
135 if (m_listenerManager != null)
136 {
137 if (active == 1)
138 m_listenerManager.Activate(handle);
139 else if (active == 0)
140 m_listenerManager.Dectivate(handle);
141 }
142 }
143
144 public void ListenRemove(int handle)
145 {
146 if (m_listenerManager != null)
147 {
148 m_listenerManager.Remove(handle);
149 }
150 }
151
152 public void DeleteListener(LLUUID itemID)
153 {
154 if (m_listenerManager != null)
155 {
156 m_listenerManager.DeleteListener(itemID);
157 }
158 }
159
160 // This method scans nearby objects and determines if they are listeners,
161 // and if so if this message fits the filter. If it does, then
162 // enqueue the message for delivery to the objects listen event handler.
163 // Objects that do an llSay have their messages delivered here, and for
164 // nearby avatars, the SimChat function is used.
165 public void DeliverMessage(string sourceItemID, ChatTypeEnum type, int channel, string name, string msg)
166 {
167 SceneObjectPart source = null;
168 ScenePresence avatar = null;
169
170 source = m_scene.GetSceneObjectPart(new LLUUID(sourceItemID));
171 if (source == null)
172 {
173 avatar = m_scene.GetScenePresence(new LLUUID(sourceItemID));
174 }
175 if ((avatar != null) || (source != null))
176 {
177 // Loop through the objects in the scene
178 // If they are in proximity, then if they are
179 // listeners, if so add them to the pending queue
180
181 foreach (ListenerInfo li in m_listenerManager.GetListeners())
182 {
183 EntityBase sPart;
184
185 m_scene.Entities.TryGetValue(li.GetHostID(), out sPart);
186
187 if (sPart != null)
188 {
189 double dis = 0;
190
191 if (source != null)
192 dis = Util.GetDistanceTo(sPart.AbsolutePosition, source.AbsolutePosition);
193 else
194 dis = Util.GetDistanceTo(sPart.AbsolutePosition, avatar.AbsolutePosition);
195
196 switch (type)
197 {
198 case ChatTypeEnum.Whisper:
199
200 if ((dis < 10) && (dis > -10))
201 {
202 if (li.GetChannel() == channel)
203 {
204 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
205 sourceItemID, sPart.UUID, channel, name, msg
206 );
207 if (isListener != null)
208 {
209 lock (m_pending.SyncRoot)
210 {
211 m_pending.Enqueue(isListener);
212 }
213 }
214 }
215 }
216 break;
217
218 case ChatTypeEnum.Say:
219
220 if ((dis < 30) && (dis > -30))
221 {
222 if (li.GetChannel() == channel)
223 {
224 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
225 sourceItemID, sPart.UUID, channel, name, msg
226 );
227 if (isListener != null)
228 {
229 lock (m_pending.SyncRoot)
230 {
231 m_pending.Enqueue(isListener);
232 }
233 }
234 }
235 }
236 break;
237
238 case ChatTypeEnum.Shout:
239 if ((dis < 100) && (dis > -100))
240 {
241 if (li.GetChannel() == channel)
242 {
243 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
244 sourceItemID, sPart.UUID, channel, name, msg
245 );
246 if (isListener != null)
247 {
248 lock (m_pending.SyncRoot)
249 {
250 m_pending.Enqueue(isListener);
251 }
252 }
253 }
254 }
255 break;
256
257 case ChatTypeEnum.Broadcast:
258 // Dont process if this message is from itself!
259 if (li.GetHostID().ToString().Equals(sourceItemID) ||
260 sPart.UUID.ToString().Equals(sourceItemID))
261 continue;
262
263 if (li.GetChannel() == channel)
264 {
265 ListenerInfo isListener = m_listenerManager.IsListenerMatch(
266 sourceItemID, sPart.UUID, channel, name, msg
267 );
268 if (isListener != null)
269 {
270 lock (m_pending.SyncRoot)
271 {
272 m_pending.Enqueue(isListener);
273 }
274 }
275 }
276
277 break;
278 }
279 }
280 }
281 }
282 }
283
284 public bool HasMessages()
285 {
286 if (m_pending != null)
287 return (m_pending.Count > 0);
288 else
289 return false;
290 }
291
292 public ListenerInfo GetNextMessage()
293 {
294 ListenerInfo li = null;
295
296 lock (m_pending.SyncRoot)
297 {
298 li = (ListenerInfo)m_pending.Dequeue();
299 }
300
301 return li;
302 }
303
304 public uint PeekNextMessageLocalID()
305 {
306 return ((ListenerInfo)m_pending.Peek()).GetLocalID();
307 }
308
309 public LLUUID PeekNextMessageItemID()
310 {
311 return ((ListenerInfo)m_pending.Peek()).GetItemID();
312 }
313 }
314
315 public class ListenerManager
316 {
317 //private Dictionary<int, ListenerInfo> m_listeners;
318 private Hashtable m_listeners = Hashtable.Synchronized(new Hashtable());
319 private object ListenersLock = new object();
320 private int m_MaxListeners = 100;
321
322 public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg)
323 {
324 if (m_listeners.Count < m_MaxListeners)
325 {
326 ListenerInfo isListener = IsListenerMatch(LLUUID.Zero.ToString(), itemID, channel, name, msg);
327
328 if (isListener == null)
329 {
330 int newHandle = GetNewHandle();
331
332 if (newHandle > -1)
333 {
334 ListenerInfo li = new ListenerInfo(localID, newHandle, itemID, hostID, channel, name, id, msg);
335
336 lock (m_listeners.SyncRoot)
337 {
338 m_listeners.Add(newHandle, li);
339 }
340
341 return newHandle;
342 }
343 }
344 }
345
346 return -1;
347 }
348
349 public void Remove(int handle)
350 {
351 lock (m_listeners.SyncRoot)
352 {
353 m_listeners.Remove(handle);
354 }
355 }
356
357 public void DeleteListener(LLUUID itemID)
358 {
359 ArrayList removedListeners = new ArrayList();
360
361 lock (m_listeners.SyncRoot)
362 {
363 IDictionaryEnumerator en = m_listeners.GetEnumerator();
364 while (en.MoveNext())
365 {
366 ListenerInfo li = (ListenerInfo)en.Value;
367 if (li.GetItemID().Equals(itemID))
368 {
369 removedListeners.Add(li.GetHandle());
370 }
371 }
372 foreach (int handle in removedListeners)
373 {
374 m_listeners.Remove(handle);
375 }
376 }
377 }
378
379 private int GetNewHandle()
380 {
381 for (int i = 0; i < int.MaxValue - 1; i++)
382 {
383 if (!m_listeners.ContainsKey(i))
384 return i;
385 }
386
387 return -1;
388 }
389
390 public bool IsListener(LLUUID hostID)
391 {
392 foreach (ListenerInfo li in m_listeners.Values)
393 {
394 if (li.GetHostID().Equals(hostID))
395 return true;
396 }
397
398 return false;
399 }
400
401 public void Activate(int handle)
402 {
403
404 if (m_listeners.ContainsKey(handle))
405 {
406 lock (m_listeners.SyncRoot)
407 {
408 ListenerInfo li = (ListenerInfo)m_listeners[handle];
409 li.Activate();
410 }
411 }
412 }
413
414 public void Dectivate(int handle)
415 {
416
417 if (m_listeners.ContainsKey(handle))
418 {
419 ListenerInfo li = (ListenerInfo)m_listeners[handle];
420 li.Deactivate();
421 }
422 }
423
424 // Theres probably a more clever and efficient way to
425 // do this, maybe with regex.
426 public ListenerInfo IsListenerMatch(string sourceItemID, LLUUID listenerKey, int channel, string name,
427 string msg)
428 {
429 bool isMatch = true;
430 lock (m_listeners.SyncRoot)
431 {
432 IDictionaryEnumerator en = m_listeners.GetEnumerator();
433 while (en.MoveNext())
434 {
435 ListenerInfo li = (ListenerInfo)en.Value;
436
437 if (li.IsActive())
438 {
439 if (li.GetHostID().Equals(listenerKey))
440 {
441 if (channel == li.GetChannel())
442 {
443 if ((li.GetID().ToString().Length > 0) &&
444 (!li.GetID().Equals(LLUUID.Zero)))
445 {
446 if (!li.GetID().ToString().Equals(sourceItemID))
447 {
448 isMatch = false;
449 }
450 }
451 if (isMatch && (li.GetName().Length > 0))
452 {
453 if (li.GetName().Equals(name))
454 {
455 isMatch = false;
456 }
457 }
458 if (isMatch)
459 {
460 return new ListenerInfo(
461 li.GetLocalID(), li.GetHandle(), li.GetItemID(), li.GetHostID(),
462 li.GetChannel(), name, li.GetID(), msg, new LLUUID(sourceItemID)
463 );
464 }
465 }
466 }
467 }
468 }
469 }
470 return null;
471 }
472
473 public ICollection GetListeners()
474 {
475 return m_listeners.Values;
476 }
477 }
478
479 public class ListenerInfo
480 {
481 private LLUUID m_itemID; // ID of the host script engine
482 private LLUUID m_hostID; // ID of the host/scene part
483 private LLUUID m_sourceItemID; // ID of the scenePart or avatar source of the message
484 private int m_channel; // Channel
485 private int m_handle; // Assigned handle of this listener
486 private uint m_localID; // Local ID from script engine
487 private string m_name; // Object name to filter messages from
488 private LLUUID m_id; // ID to filter messages from
489 private string m_message; // The message
490 private bool m_active; // Listener is active or not
491
492 public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message)
493 {
494 Initialise(localID, handle, ItemID, hostID, channel, name, id, message);
495 }
496
497 public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id,
498 string message, LLUUID sourceItemID)
499 {
500 Initialise(localID, handle, ItemID, hostID, channel, name, id, message);
501 m_sourceItemID = sourceItemID;
502 }
503
504 private void Initialise(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name,
505 LLUUID id, string message)
506 {
507 m_handle = handle;
508 m_channel = channel;
509 m_itemID = ItemID;
510 m_hostID = hostID;
511 m_name = name;
512 m_id = id;
513 m_message = message;
514 m_active = true;
515 m_localID = localID;
516 }
517
518 public LLUUID GetItemID()
519 {
520 return m_itemID;
521 }
522
523 public LLUUID GetHostID()
524 {
525 return m_hostID;
526 }
527
528 public LLUUID GetSourceItemID()
529 {
530 return m_sourceItemID;
531 }
532
533 public int GetChannel()
534 {
535 return m_channel;
536 }
537
538 public uint GetLocalID()
539 {
540 return m_localID;
541 }
542
543 public int GetHandle()
544 {
545 return m_handle;
546 }
547
548 public string GetMessage()
549 {
550 return m_message;
551 }
552
553 public string GetName()
554 {
555 return m_name;
556 }
557
558 public bool IsActive()
559 {
560 return m_active;
561 }
562
563 public void Deactivate()
564 {
565 m_active = false;
566 }
567
568 public void Activate()
569 {
570 m_active = true;
571 }
572
573 public LLUUID GetID()
574 {
575 return m_id;
576 }
577 }
578} \ No newline at end of file