diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Environment/Modules/WorldCommModule.cs | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/OpenSim/Region/Environment/Modules/WorldCommModule.cs b/OpenSim/Region/Environment/Modules/WorldCommModule.cs new file mode 100644 index 0000000..c2ec699 --- /dev/null +++ b/OpenSim/Region/Environment/Modules/WorldCommModule.cs | |||
@@ -0,0 +1,481 @@ | |||
1 | using System; | ||
2 | using System.IO; | ||
3 | using System.Net.Sockets; | ||
4 | using System.Text; | ||
5 | using System.Threading; | ||
6 | using libsecondlife; | ||
7 | using OpenSim.Framework.Interfaces; | ||
8 | using OpenSim.Framework.Utilities; | ||
9 | using OpenSim.Region.Environment.Interfaces; | ||
10 | using OpenSim.Region.Environment.Scenes; | ||
11 | using OpenSim.Framework.Servers; | ||
12 | using Nwc.XmlRpc; | ||
13 | using System.Collections; | ||
14 | using System.Collections.Generic; | ||
15 | |||
16 | /***************************************************** | ||
17 | * | ||
18 | * WorldCommModule | ||
19 | * | ||
20 | * | ||
21 | * Holding place for world comms - basically llListen | ||
22 | * function implementation. | ||
23 | * | ||
24 | * lLListen(integer channel, string name, key id, string msg) | ||
25 | * The name, id, and msg arguments specify the filtering | ||
26 | * criteria. You can pass the empty string | ||
27 | * (or NULL_KEY for id) for these to set a completely | ||
28 | * open filter; this causes the listen() event handler to be | ||
29 | * invoked for all chat on the channel. To listen only | ||
30 | * for chat spoken by a specific object or avatar, | ||
31 | * specify the name and/or id arguments. To listen | ||
32 | * only for a specific command, specify the | ||
33 | * (case-sensitive) msg argument. If msg is not empty, | ||
34 | * listener will only hear strings which are exactly equal | ||
35 | * to msg. You can also use all the arguments to establish | ||
36 | * the most restrictive filtering criteria. | ||
37 | * | ||
38 | * It might be useful for each listener to maintain a message | ||
39 | * digest, with a list of recent messages by UUID. This can | ||
40 | * be used to prevent in-world repeater loops. However, the | ||
41 | * linden functions do not have this capability, so for now | ||
42 | * thats the way it works. | ||
43 | * | ||
44 | * **************************************************/ | ||
45 | namespace OpenSim.Region.Environment.Modules | ||
46 | { | ||
47 | public class WorldCommModule : IRegionModule, IWorldComm | ||
48 | { | ||
49 | private Scene m_scene; | ||
50 | private object CommListLock = new object(); | ||
51 | private string m_name = "WorldCommModule"; | ||
52 | private ListenerManager m_listenerManager; | ||
53 | private Queue<ListenerInfo> m_pending; | ||
54 | |||
55 | public WorldCommModule() | ||
56 | { | ||
57 | } | ||
58 | |||
59 | public void Initialise(Scene scene) | ||
60 | { | ||
61 | |||
62 | m_scene = scene; | ||
63 | m_scene.RegisterModuleInterface<IWorldComm>(this); | ||
64 | m_listenerManager = new ListenerManager(); | ||
65 | m_pending = new Queue<ListenerInfo>(); | ||
66 | m_scene.EventManager.OnNewClient += NewClient; | ||
67 | |||
68 | } | ||
69 | |||
70 | public void PostInitialise() | ||
71 | { | ||
72 | } | ||
73 | |||
74 | public void CloseDown() | ||
75 | { | ||
76 | } | ||
77 | |||
78 | public string GetName() | ||
79 | { | ||
80 | return m_name; | ||
81 | } | ||
82 | |||
83 | public bool IsSharedModule() | ||
84 | { | ||
85 | return false; | ||
86 | } | ||
87 | |||
88 | public void NewClient(IClientAPI client) | ||
89 | { | ||
90 | client.OnChatFromViewer += DeliverClientMessage; | ||
91 | } | ||
92 | |||
93 | private void DeliverClientMessage(byte[] message, byte type, int channel, LLVector3 fromPos, string fromName, | ||
94 | LLUUID fromAgentID) | ||
95 | { | ||
96 | ASCIIEncoding ae = new ASCIIEncoding(); | ||
97 | |||
98 | DeliverMessage(fromAgentID.ToString(), type, channel, fromName, ae.GetString(message)); | ||
99 | |||
100 | } | ||
101 | |||
102 | public int Listen(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) | ||
103 | { | ||
104 | return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); | ||
105 | } | ||
106 | |||
107 | public void ListenControl(int handle, int active) | ||
108 | { | ||
109 | if ( active == 1 ) | ||
110 | m_listenerManager.Activate(handle); | ||
111 | else if ( active == 0 ) | ||
112 | m_listenerManager.Dectivate(handle); | ||
113 | |||
114 | } | ||
115 | |||
116 | public void ListenRemove(int handle) | ||
117 | { | ||
118 | m_listenerManager.Remove(handle); | ||
119 | } | ||
120 | |||
121 | // This method scans nearby objects and determines if they are listeners, | ||
122 | // and if so if this message fits the filter. If it does, then | ||
123 | // enqueue the message for delivery to the objects listen event handler. | ||
124 | // Objects that do an llSay have their messages delivered here, and for | ||
125 | // nearby avatards, the SimChat function is used. | ||
126 | public void DeliverMessage(string sourceItemID, int type, int channel, string name, string msg) | ||
127 | { | ||
128 | |||
129 | SceneObjectPart source = null; | ||
130 | ScenePresence avatar = null; | ||
131 | |||
132 | source = m_scene.GetSceneObjectPart(new LLUUID(sourceItemID)); | ||
133 | if (source == null) | ||
134 | { | ||
135 | avatar = m_scene.GetScenePresence(new LLUUID(sourceItemID)); | ||
136 | } | ||
137 | if( (avatar != null) || (source != null) ) | ||
138 | { | ||
139 | // Loop through the objects in the scene | ||
140 | // If they are in proximity, then if they are | ||
141 | // listeners, if so add them to the pending queue | ||
142 | |||
143 | foreach (LLUUID eb in m_scene.Entities.Keys) | ||
144 | { | ||
145 | EntityBase sPart; | ||
146 | |||
147 | m_scene.Entities.TryGetValue(eb, out sPart); | ||
148 | |||
149 | // Dont process if this message is from itself! | ||
150 | if (eb.ToString().Equals(sourceItemID) || | ||
151 | sPart.UUID.ToString().Equals(sourceItemID) ) | ||
152 | continue; | ||
153 | |||
154 | double dis = 0; | ||
155 | |||
156 | if (source != null) | ||
157 | dis = sPart.AbsolutePosition.GetDistanceTo(source.AbsolutePosition); | ||
158 | else | ||
159 | dis = sPart.AbsolutePosition.GetDistanceTo(avatar.AbsolutePosition); | ||
160 | |||
161 | switch (type) | ||
162 | { | ||
163 | case 0: // Whisper | ||
164 | |||
165 | if ((dis < 10) && (dis > -10)) | ||
166 | { | ||
167 | ListenerInfo isListener = m_listenerManager.IsListenerMatch( | ||
168 | sourceItemID, sPart.UUID, channel, name, msg | ||
169 | ); | ||
170 | if (isListener != null) | ||
171 | { | ||
172 | m_pending.Enqueue(isListener); | ||
173 | } | ||
174 | |||
175 | } | ||
176 | break; | ||
177 | |||
178 | case 1: // Say | ||
179 | |||
180 | if ((dis < 30) && (dis > -30)) | ||
181 | { | ||
182 | ListenerInfo isListener = m_listenerManager.IsListenerMatch( | ||
183 | sourceItemID, sPart.UUID, channel, name, msg | ||
184 | ); | ||
185 | if (isListener != null) | ||
186 | { | ||
187 | m_pending.Enqueue(isListener); | ||
188 | } | ||
189 | |||
190 | } | ||
191 | break; | ||
192 | |||
193 | case 2: // Shout | ||
194 | if ((dis < 100) && (dis > -100)) | ||
195 | { | ||
196 | ListenerInfo isListener = m_listenerManager.IsListenerMatch( | ||
197 | sourceItemID, sPart.UUID, channel, name, msg | ||
198 | ); | ||
199 | if (isListener != null) | ||
200 | { | ||
201 | m_pending.Enqueue(isListener); | ||
202 | } | ||
203 | |||
204 | } | ||
205 | break; | ||
206 | |||
207 | case 0xff: // Broadcast | ||
208 | ListenerInfo isListen = m_listenerManager.IsListenerMatch(sourceItemID, eb, channel, name, msg); | ||
209 | if (isListen != null) | ||
210 | { | ||
211 | ListenerInfo isListener = m_listenerManager.IsListenerMatch( | ||
212 | sourceItemID, sPart.UUID, channel, name, msg | ||
213 | ); | ||
214 | if (isListener != null) | ||
215 | { | ||
216 | m_pending.Enqueue(isListener); | ||
217 | } | ||
218 | } | ||
219 | break; | ||
220 | } | ||
221 | }; | ||
222 | |||
223 | } | ||
224 | |||
225 | } | ||
226 | |||
227 | public bool HasMessages() | ||
228 | { | ||
229 | return (m_pending.Count > 0); | ||
230 | } | ||
231 | |||
232 | public ListenerInfo GetNextMessage() | ||
233 | { | ||
234 | |||
235 | ListenerInfo li = null; | ||
236 | |||
237 | lock (CommListLock) | ||
238 | { | ||
239 | li = m_pending.Dequeue(); | ||
240 | } | ||
241 | |||
242 | return li; | ||
243 | |||
244 | } | ||
245 | |||
246 | } | ||
247 | |||
248 | // hostID: the ID of the ScenePart | ||
249 | // itemID: the ID of the script host engine | ||
250 | // localID: local ID of host engine | ||
251 | public class ListenerManager | ||
252 | { | ||
253 | private Dictionary<int, ListenerInfo> m_listeners; | ||
254 | private object ListenersLock = new object(); | ||
255 | private int m_MaxListeners = 100; | ||
256 | |||
257 | public ListenerManager() | ||
258 | { | ||
259 | m_listeners = new Dictionary<int, ListenerInfo>(); | ||
260 | } | ||
261 | |||
262 | public int AddListener(uint localID, LLUUID itemID, LLUUID hostID, int channel, string name, string id, string msg) | ||
263 | { | ||
264 | |||
265 | if ( m_listeners.Count < m_MaxListeners ) | ||
266 | { | ||
267 | ListenerInfo isListener = IsListenerMatch(LLUUID.Zero.ToString(), itemID, channel, name, msg); | ||
268 | |||
269 | if(isListener == null) | ||
270 | { | ||
271 | int newHandle = GetNewHandle(); | ||
272 | |||
273 | if (newHandle > -1) | ||
274 | { | ||
275 | |||
276 | ListenerInfo li = new ListenerInfo(localID, newHandle, itemID, hostID, channel, name, id, msg); | ||
277 | |||
278 | lock (ListenersLock) | ||
279 | { | ||
280 | m_listeners.Add(newHandle, li); | ||
281 | } | ||
282 | |||
283 | return newHandle; | ||
284 | } | ||
285 | |||
286 | } | ||
287 | |||
288 | } | ||
289 | |||
290 | return -1; | ||
291 | |||
292 | } | ||
293 | |||
294 | public void Remove(int handle) | ||
295 | { | ||
296 | m_listeners.Remove(handle); | ||
297 | } | ||
298 | |||
299 | private int GetNewHandle() | ||
300 | { | ||
301 | |||
302 | for (int i = 0; i < int.MaxValue - 1; i++) | ||
303 | { | ||
304 | if (!m_listeners.ContainsKey(i)) | ||
305 | return i; | ||
306 | } | ||
307 | |||
308 | return -1; | ||
309 | |||
310 | } | ||
311 | |||
312 | public bool IsListener(LLUUID hostID) | ||
313 | { | ||
314 | |||
315 | foreach (ListenerInfo li in m_listeners.Values) | ||
316 | { | ||
317 | if (li.GetHostID().Equals(hostID)) | ||
318 | return true; | ||
319 | } | ||
320 | |||
321 | return false; | ||
322 | |||
323 | } | ||
324 | |||
325 | public void Activate(int handle) | ||
326 | { | ||
327 | |||
328 | ListenerInfo li; | ||
329 | |||
330 | if( m_listeners.TryGetValue(handle, out li) ) | ||
331 | { | ||
332 | li.Activate(); | ||
333 | } | ||
334 | } | ||
335 | |||
336 | public void Dectivate(int handle) | ||
337 | { | ||
338 | |||
339 | ListenerInfo li; | ||
340 | |||
341 | if( m_listeners.TryGetValue(handle, out li) ) | ||
342 | { | ||
343 | li.Deactivate(); | ||
344 | } | ||
345 | } | ||
346 | |||
347 | // Theres probably a more clever and efficient way to | ||
348 | // do this, maybe with regex. | ||
349 | public ListenerInfo IsListenerMatch(string sourceItemID, LLUUID listenerKey, int channel, string name, string msg) | ||
350 | { | ||
351 | |||
352 | bool isMatch = true; | ||
353 | |||
354 | foreach (ListenerInfo li in m_listeners.Values) | ||
355 | { | ||
356 | if (li.GetHostID().Equals(listenerKey)) | ||
357 | { | ||
358 | if ( li.IsActive() ) | ||
359 | { | ||
360 | if ( channel == li.GetChannel() ) | ||
361 | { | ||
362 | if ( (li.GetID().ToString().Length > 0) && | ||
363 | (!li.GetID().Equals(LLUUID.Zero)) ) | ||
364 | { | ||
365 | if (!li.GetID().ToString().Equals(sourceItemID)) | ||
366 | { | ||
367 | isMatch = false; | ||
368 | } | ||
369 | } | ||
370 | if ( isMatch && (li.GetName().Length > 0) ) | ||
371 | { | ||
372 | if ( li.GetName().Equals(name) ) | ||
373 | { | ||
374 | isMatch = false; | ||
375 | } | ||
376 | } | ||
377 | if ( isMatch ) | ||
378 | { | ||
379 | return new ListenerInfo( | ||
380 | li.GetLocalID(), li.GetHandle(), li.GetItemID(), li.GetHostID(), | ||
381 | li.GetChannel(), name, li.GetID(), msg, new LLUUID(sourceItemID) | ||
382 | ); | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | return null; | ||
389 | } | ||
390 | |||
391 | } | ||
392 | |||
393 | public class ListenerInfo | ||
394 | { | ||
395 | |||
396 | private LLUUID m_itemID; // ID of the host script engine | ||
397 | private LLUUID m_hostID; // ID of the host/scene part | ||
398 | private LLUUID m_sourceItemID; // ID of the scenePart or avatar source of the message | ||
399 | private int m_channel; // Channel | ||
400 | private int m_handle; // Assigned handle of this listener | ||
401 | private uint m_localID; // Local ID from script engine | ||
402 | private string m_name; // Object name to filter messages from | ||
403 | private LLUUID m_id; // ID to filter messages from | ||
404 | private string m_message; // The message | ||
405 | private bool m_active; // Listener is active or not | ||
406 | |||
407 | public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message) | ||
408 | { | ||
409 | Initialise(localID, handle, ItemID, hostID, channel, name, id, message); | ||
410 | } | ||
411 | |||
412 | public ListenerInfo(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message, LLUUID sourceItemID) | ||
413 | { | ||
414 | Initialise(localID, handle, ItemID, hostID, channel, name, id, message); | ||
415 | m_sourceItemID = sourceItemID; | ||
416 | } | ||
417 | |||
418 | private void Initialise(uint localID, int handle, LLUUID ItemID, LLUUID hostID, int channel, string name, LLUUID id, string message) | ||
419 | { | ||
420 | m_handle = handle; | ||
421 | m_channel = channel; | ||
422 | m_itemID = ItemID; | ||
423 | m_hostID = hostID; | ||
424 | m_name = name; | ||
425 | m_id = id; | ||
426 | m_message = message; | ||
427 | m_active = true; | ||
428 | m_localID = localID; | ||
429 | } | ||
430 | public LLUUID GetItemID() | ||
431 | { | ||
432 | return m_itemID; | ||
433 | } | ||
434 | public LLUUID GetHostID() | ||
435 | { | ||
436 | return m_hostID; | ||
437 | } | ||
438 | public LLUUID GetSourceItemID() | ||
439 | { | ||
440 | return m_sourceItemID; | ||
441 | } | ||
442 | public int GetChannel() | ||
443 | { | ||
444 | return m_channel; | ||
445 | } | ||
446 | public uint GetLocalID() | ||
447 | { | ||
448 | return m_localID; | ||
449 | } | ||
450 | public int GetHandle() | ||
451 | { | ||
452 | return m_handle; | ||
453 | } | ||
454 | public string GetMessage() | ||
455 | { | ||
456 | return m_message; | ||
457 | } | ||
458 | public string GetName() | ||
459 | { | ||
460 | return m_name; | ||
461 | } | ||
462 | public bool IsActive() | ||
463 | { | ||
464 | return m_active; | ||
465 | } | ||
466 | public void Deactivate() | ||
467 | { | ||
468 | m_active = false; | ||
469 | } | ||
470 | public void Activate() | ||
471 | { | ||
472 | m_active = true; | ||
473 | } | ||
474 | public LLUUID GetID() | ||
475 | { | ||
476 | return m_id; | ||
477 | } | ||
478 | |||
479 | } | ||
480 | |||
481 | } | ||