clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
28 using System;
29 using System.Collections.Generic;
30 using System.Linq;
31 using System.Reflection;
32 using log4net;
33 using Mono.Addins;
34 using Nini.Config;
35 using OpenMetaverse;
36 using OpenMetaverse.StructuredData;
37 using OpenSim.Framework;
38 using OpenSim.Region.Framework.Interfaces;
39 using OpenSim.Region.Framework.Scenes;
40 using OpenSim.Services.Interfaces;
41 using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
42  
43 namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
44 {
45 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")]
46 public class GroupsMessagingModule : ISharedRegionModule, IGroupsMessagingModule
47 {
48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
49  
50 private List<Scene> m_sceneList = new List<Scene>();
51 private IPresenceService m_presenceService;
52  
53 private IMessageTransferModule m_msgTransferModule = null;
54  
55 private IGroupsServicesConnector m_groupData = null;
56  
57 // Config Options
58 private bool m_groupMessagingEnabled;
59 private bool m_debugEnabled;
60  
61 /// <summary>
62 /// If enabled, module only tries to send group IMs to online users by querying cached presence information.
63 /// </summary>
64 private bool m_messageOnlineAgentsOnly;
65  
66 /// <summary>
67 /// Cache for online users.
68 /// </summary>
69 /// <remarks>
70 /// Group ID is key, presence information for online members is value.
71 /// Will only be non-null if m_messageOnlineAgentsOnly = true
72 /// We cache here so that group messages don't constantly have to re-request the online user list to avoid
73 /// attempted expensive sending of messages to offline users.
74 /// The tradeoff is that a user that comes online will not receive messages consistently from all other users
75 /// until caches have updated.
76 /// Therefore, we set the cache expiry to just 20 seconds.
77 /// </remarks>
78 private ExpiringCache<UUID, PresenceInfo[]> m_usersOnlineCache;
79  
80 private int m_usersOnlineCacheExpirySeconds = 20;
81  
82 #region Region Module interfaceBase Members
83  
84 public void Initialise(IConfigSource config)
85 {
86 IConfig groupsConfig = config.Configs["Groups"];
87  
88 if (groupsConfig == null)
89 {
90 // Do not run this module by default.
91 return;
92 }
93 else
94 {
95 // if groups aren't enabled, we're not needed.
96 // if we're not specified as the connector to use, then we're not wanted
97 if ((groupsConfig.GetBoolean("Enabled", false) == false)
98 || (groupsConfig.GetString("MessagingModule", "") != Name))
99 {
100 m_groupMessagingEnabled = false;
101 return;
102 }
103  
104 m_groupMessagingEnabled = groupsConfig.GetBoolean("MessagingEnabled", true);
105  
106 if (!m_groupMessagingEnabled)
107 {
108 return;
109 }
110  
111 m_messageOnlineAgentsOnly = groupsConfig.GetBoolean("MessageOnlineUsersOnly", false);
112  
113 if (m_messageOnlineAgentsOnly)
114 m_usersOnlineCache = new ExpiringCache<UUID, PresenceInfo[]>();
115  
116 m_debugEnabled = groupsConfig.GetBoolean("MessagingDebugEnabled", m_debugEnabled);
117 }
118  
119 m_log.InfoFormat(
120 "[GROUPS-MESSAGING]: GroupsMessagingModule enabled with MessageOnlineOnly = {0}, DebugEnabled = {1}",
121 m_messageOnlineAgentsOnly, m_debugEnabled);
122 }
123  
124 public void AddRegion(Scene scene)
125 {
126 if (!m_groupMessagingEnabled)
127 return;
128  
129 scene.RegisterModuleInterface<IGroupsMessagingModule>(this);
130  
131 scene.AddCommand(
132 "Debug",
133 this,
134 "debug groups messaging verbose",
135 "debug groups messaging verbose <true|false>",
136 "This setting turns on very verbose groups messaging debugging",
137 HandleDebugGroupsMessagingVerbose);
138 }
139  
140 public void RegionLoaded(Scene scene)
141 {
142 if (!m_groupMessagingEnabled)
143 return;
144  
145 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
146  
147 m_groupData = scene.RequestModuleInterface<IGroupsServicesConnector>();
148  
149 // No groups module, no groups messaging
150 if (m_groupData == null)
151 {
152 m_log.Error("[GROUPS-MESSAGING]: Could not get IGroupsServicesConnector, GroupsMessagingModule is now disabled.");
153 Close();
154 m_groupMessagingEnabled = false;
155 return;
156 }
157  
158 m_msgTransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
159  
160 // No message transfer module, no groups messaging
161 if (m_msgTransferModule == null)
162 {
163 m_log.Error("[GROUPS-MESSAGING]: Could not get MessageTransferModule");
164 Close();
165 m_groupMessagingEnabled = false;
166 return;
167 }
168  
169 if (m_presenceService == null)
170 m_presenceService = scene.PresenceService;
171  
172 m_sceneList.Add(scene);
173  
174 scene.EventManager.OnNewClient += OnNewClient;
175 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
176 scene.EventManager.OnClientLogin += OnClientLogin;
177 }
178  
179 public void RemoveRegion(Scene scene)
180 {
181 if (!m_groupMessagingEnabled)
182 return;
183  
184 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
185  
186 m_sceneList.Remove(scene);
187 }
188  
189 public void Close()
190 {
191 if (!m_groupMessagingEnabled)
192 return;
193  
194 if (m_debugEnabled) m_log.Debug("[GROUPS-MESSAGING]: Shutting down GroupsMessagingModule module.");
195  
196 foreach (Scene scene in m_sceneList)
197 {
198 scene.EventManager.OnNewClient -= OnNewClient;
199 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
200 }
201  
202 m_sceneList.Clear();
203  
204 m_groupData = null;
205 m_msgTransferModule = null;
206 }
207  
208 public Type ReplaceableInterface
209 {
210 get { return null; }
211 }
212  
213 public string Name
214 {
215 get { return "GroupsMessagingModule"; }
216 }
217  
218 #endregion
219  
220 #region ISharedRegionModule Members
221  
222 public void PostInitialise()
223 {
224 // NoOp
225 }
226  
227 #endregion
228  
229 private void HandleDebugGroupsMessagingVerbose(object modules, string[] args)
230 {
231 if (args.Length < 5)
232 {
233 MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
234 return;
235 }
236  
237 bool verbose = false;
238 if (!bool.TryParse(args[4], out verbose))
239 {
240 MainConsole.Instance.Output("Usage: debug groups messaging verbose <true|false>");
241 return;
242 }
243  
244 m_debugEnabled = verbose;
245  
246 MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled);
247 }
248  
249 /// <summary>
250 /// Not really needed, but does confirm that the group exists.
251 /// </summary>
252 public bool StartGroupChatSession(UUID agentID, UUID groupID)
253 {
254 if (m_debugEnabled)
255 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
256  
257 GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID, groupID, null);
258  
259 if (groupInfo != null)
260 {
261 return true;
262 }
263 else
264 {
265 return false;
266 }
267 }
268  
269 public void SendMessageToGroup(GridInstantMessage im, UUID groupID)
270 {
271 SendMessageToGroup(im, groupID, new UUID(im.fromAgentID), null);
272 }
273  
274 public void SendMessageToGroup(
275 GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func<GroupMembersData, bool> sendCondition)
276 {
277 int requestStartTick = Environment.TickCount;
278  
279 List<GroupMembersData> groupMembers = m_groupData.GetGroupMembers(sendingAgentForGroupCalls, groupID);
280 int groupMembersCount = groupMembers.Count;
281 HashSet<string> attemptDeliveryUuidSet = null;
282  
283 if (m_messageOnlineAgentsOnly)
284 {
285 string[] t1 = groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()).ToArray();
286  
287 // We cache in order not to overwhlem the presence service on large grids with many groups. This does
288 // mean that members coming online will not see all group members until after m_usersOnlineCacheExpirySeconds has elapsed.
289 // (assuming this is the same across all grid simulators).
290 PresenceInfo[] onlineAgents;
291 if (!m_usersOnlineCache.TryGetValue(groupID, out onlineAgents))
292 {
293 onlineAgents = m_presenceService.GetAgents(t1);
294 m_usersOnlineCache.Add(groupID, onlineAgents, m_usersOnlineCacheExpirySeconds);
295 }
296  
297 attemptDeliveryUuidSet
298 = new HashSet<string>(Array.ConvertAll<PresenceInfo, string>(onlineAgents, pi => pi.UserID));
299  
300 //Array.ForEach<PresenceInfo>(onlineAgents, pi => attemptDeliveryUuidSet.Add(pi.UserID));
301  
302 //groupMembers = groupMembers.Where(gmd => onlineAgentsUuidSet.Contains(gmd.AgentID.ToString())).ToList();
303  
304 // if (m_debugEnabled)
305 // m_log.DebugFormat(
306 // "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members, {2} online",
307 // groupID, groupMembersCount, groupMembers.Count());
308 }
309 else
310 {
311 attemptDeliveryUuidSet
312 = new HashSet<string>(groupMembers.ConvertAll<string>(gmd => gmd.AgentID.ToString()));
313  
314 if (m_debugEnabled)
315 m_log.DebugFormat(
316 "[GROUPS-MESSAGING]: SendMessageToGroup called for group {0} with {1} visible members",
317 groupID, groupMembers.Count);
318 }
319  
320 foreach (GroupMembersData member in groupMembers)
321 {
322 if (sendCondition != null)
323 {
324 if (!sendCondition(member))
325 {
326 if (m_debugEnabled)
327 m_log.DebugFormat(
328 "[GROUPS-MESSAGING]: Not sending to {0} as they do not fulfill send condition",
329 member.AgentID);
330  
331 continue;
332 }
333 }
334 else if (m_groupData.hasAgentDroppedGroupChatSession(member.AgentID, groupID))
335 {
336 // Don't deliver messages to people who have dropped this session
337 if (m_debugEnabled)
338 m_log.DebugFormat(
339 "[GROUPS-MESSAGING]: {0} has dropped session, not delivering to them", member.AgentID);
340  
341 continue;
342 }
343  
344 // Copy Message
345 GridInstantMessage msg = new GridInstantMessage();
346 msg.imSessionID = im.imSessionID;
347 msg.fromAgentName = im.fromAgentName;
348 msg.message = im.message;
349 msg.dialog = im.dialog;
350 msg.offline = im.offline;
351 msg.ParentEstateID = im.ParentEstateID;
352 msg.Position = im.Position;
353 msg.RegionID = im.RegionID;
354 msg.binaryBucket = im.binaryBucket;
355 msg.timestamp = (uint)Util.UnixTimeSinceEpoch();
356  
357 msg.fromAgentID = im.fromAgentID;
358 msg.fromGroup = true;
359  
360 msg.toAgentID = member.AgentID.Guid;
361  
362 if (attemptDeliveryUuidSet.Contains(member.AgentID.ToString()))
363 {
364 IClientAPI client = GetActiveClient(member.AgentID);
365 if (client == null)
366 {
367 int startTick = Environment.TickCount;
368  
369 // If they're not local, forward across the grid
370 m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { });
371  
372 if (m_debugEnabled)
373 m_log.DebugFormat(
374 "[GROUPS-MESSAGING]: Delivering to {0} via grid took {1} ms",
375 member.AgentID, Environment.TickCount - startTick);
376 }
377 else
378 {
379 int startTick = Environment.TickCount;
380  
381 ProcessMessageFromGroupSession(msg, client);
382  
383 // Deliver locally, directly
384 if (m_debugEnabled)
385 m_log.DebugFormat(
386 "[GROUPS-MESSAGING]: Delivering to {0} locally took {1} ms",
387 member.AgentID, Environment.TickCount - startTick);
388 }
389 }
390 else
391 {
392 int startTick = Environment.TickCount;
393  
394 m_msgTransferModule.HandleUndeliverableMessage(msg, delegate(bool success) { });
395  
396 if (m_debugEnabled)
397 m_log.DebugFormat(
398 "[GROUPS-MESSAGING]: Handling undeliverable message for {0} took {1} ms",
399 member.AgentID, Environment.TickCount - startTick);
400 }
401 }
402  
403 if (m_debugEnabled)
404 m_log.DebugFormat(
405 "[GROUPS-MESSAGING]: Total SendMessageToGroup for group {0} with {1} members, {2} candidates for delivery took {3} ms",
406 groupID, groupMembersCount, attemptDeliveryUuidSet.Count(), Environment.TickCount - requestStartTick);
407 }
408  
409 #region SimGridEventHandlers
410  
411 void OnClientLogin(IClientAPI client)
412 {
413 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
414 }
415  
416 private void OnNewClient(IClientAPI client)
417 {
418 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: OnInstantMessage registered for {0}", client.Name);
419  
420 client.OnInstantMessage += OnInstantMessage;
421 }
422  
423 private void OnGridInstantMessage(GridInstantMessage msg)
424 {
425 // The instant message module will only deliver messages of dialog types:
426 // MessageFromAgent, StartTyping, StopTyping, MessageFromObject
427 //
428 // Any other message type will not be delivered to a client by the
429 // Instant Message Module
430  
431 if (m_debugEnabled)
432 {
433 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
434  
435 DebugGridInstantMessage(msg);
436 }
437  
438 // Incoming message from a group
439 if ((msg.fromGroup == true) &&
440 ((msg.dialog == (byte)InstantMessageDialog.SessionSend)
441 || (msg.dialog == (byte)InstantMessageDialog.SessionAdd)
442 || (msg.dialog == (byte)InstantMessageDialog.SessionDrop)))
443 {
444 IClientAPI client = null;
445  
446 if (msg.dialog == (byte)InstantMessageDialog.SessionSend)
447 {
448 client = GetActiveClient(new UUID(msg.toAgentID));
449  
450 if (client != null)
451 {
452 if (m_debugEnabled)
453 m_log.DebugFormat("[GROUPS-MESSAGING]: Delivering to {0} locally", client.Name);
454 }
455 else
456 {
457 m_log.WarnFormat("[GROUPS-MESSAGING]: Received a message over the grid for a client that isn't here: {0}", msg.toAgentID);
458  
459 return;
460 }
461 }
462  
463 ProcessMessageFromGroupSession(msg, client);
464 }
465 }
466  
467 private void ProcessMessageFromGroupSession(GridInstantMessage msg, IClientAPI client)
468 {
469 if (m_debugEnabled)
470 m_log.DebugFormat(
471 "[GROUPS-MESSAGING]: Session message from {0} going to agent {1}, sessionID {2}, type {3}",
472 msg.fromAgentName, msg.toAgentID, msg.imSessionID, (InstantMessageDialog)msg.dialog);
473  
474 UUID AgentID = new UUID(msg.fromAgentID);
475 UUID GroupID = new UUID(msg.imSessionID);
476  
477 switch (msg.dialog)
478 {
479 case (byte)InstantMessageDialog.SessionAdd:
480 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
481 break;
482  
483 case (byte)InstantMessageDialog.SessionDrop:
484 m_groupData.AgentDroppedFromGroupChatSession(AgentID, GroupID);
485 break;
486  
487 case (byte)InstantMessageDialog.SessionSend:
488 if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID)
489 && !m_groupData.hasAgentBeenInvitedToGroupChatSession(AgentID, GroupID)
490 )
491 {
492 // Agent not in session and hasn't dropped from session
493 // Add them to the session for now, and Invite them
494 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
495  
496 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
497 if (groupInfo != null)
498 {
499 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: Sending chatterbox invite instant message");
500  
501 // Force? open the group session dialog???
502 // and simultanously deliver the message, so we don't need to do a seperate client.SendInstantMessage(msg);
503 IEventQueue eq = client.Scene.RequestModuleInterface<IEventQueue>();
504 eq.ChatterboxInvitation(
505 GroupID
506 , groupInfo.GroupName
507 , new UUID(msg.fromAgentID)
508 , msg.message
509 , new UUID(msg.toAgentID)
510 , msg.fromAgentName
511 , msg.dialog
512 , msg.timestamp
513 , msg.offline == 1
514 , (int)msg.ParentEstateID
515 , msg.Position
516 , 1
517 , new UUID(msg.imSessionID)
518 , msg.fromGroup
519 , Utils.StringToBytes(groupInfo.GroupName)
520 );
521  
522 eq.ChatterBoxSessionAgentListUpdates(
523 new UUID(GroupID)
524 , new UUID(msg.fromAgentID)
525 , new UUID(msg.toAgentID)
526 , false //canVoiceChat
527 , false //isModerator
528 , false //text mute
529 );
530 }
531  
532 break;
533 }
534 else if (!m_groupData.hasAgentDroppedGroupChatSession(AgentID, GroupID))
535 {
536 // User hasn't dropped, so they're in the session,
537 // maybe we should deliver it.
538 client.SendInstantMessage(msg);
539 }
540  
541 break;
542  
543 default:
544 client.SendInstantMessage(msg);
545  
546 break;;
547 }
548 }
549  
550 #endregion
551  
552 #region ClientEvents
553 private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im)
554 {
555 if (m_debugEnabled)
556 {
557 m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
558  
559 DebugGridInstantMessage(im);
560 }
561  
562 // Start group IM session
563 if ((im.dialog == (byte)InstantMessageDialog.SessionGroupStart))
564 {
565 if (m_debugEnabled) m_log.InfoFormat("[GROUPS-MESSAGING]: imSessionID({0}) toAgentID({1})", im.imSessionID, im.toAgentID);
566  
567 UUID GroupID = new UUID(im.imSessionID);
568 UUID AgentID = new UUID(im.fromAgentID);
569  
570 GroupRecord groupInfo = m_groupData.GetGroupRecord(UUID.Zero, GroupID, null);
571  
572 if (groupInfo != null)
573 {
574 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
575  
576 ChatterBoxSessionStartReplyViaCaps(remoteClient, groupInfo.GroupName, GroupID);
577  
578 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
579 queue.ChatterBoxSessionAgentListUpdates(
580 GroupID
581 , AgentID
582 , new UUID(im.toAgentID)
583 , false //canVoiceChat
584 , false //isModerator
585 , false //text mute
586 );
587 }
588 }
589  
590 // Send a message from locally connected client to a group
591 if ((im.dialog == (byte)InstantMessageDialog.SessionSend))
592 {
593 UUID GroupID = new UUID(im.imSessionID);
594 UUID AgentID = new UUID(im.fromAgentID);
595  
596 if (m_debugEnabled)
597 m_log.DebugFormat("[GROUPS-MESSAGING]: Send message to session for group {0} with session ID {1}", GroupID, im.imSessionID.ToString());
598  
599 //If this agent is sending a message, then they want to be in the session
600 m_groupData.AgentInvitedToGroupChatSession(AgentID, GroupID);
601  
602 SendMessageToGroup(im, GroupID);
603 }
604 }
605  
606 #endregion
607  
608 void ChatterBoxSessionStartReplyViaCaps(IClientAPI remoteClient, string groupName, UUID groupID)
609 {
610 if (m_debugEnabled) m_log.DebugFormat("[GROUPS-MESSAGING]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name);
611  
612 OSDMap moderatedMap = new OSDMap(4);
613 moderatedMap.Add("voice", OSD.FromBoolean(false));
614  
615 OSDMap sessionMap = new OSDMap(4);
616 sessionMap.Add("moderated_mode", moderatedMap);
617 sessionMap.Add("session_name", OSD.FromString(groupName));
618 sessionMap.Add("type", OSD.FromInteger(0));
619 sessionMap.Add("voice_enabled", OSD.FromBoolean(false));
620  
621 OSDMap bodyMap = new OSDMap(4);
622 bodyMap.Add("session_id", OSD.FromUUID(groupID));
623 bodyMap.Add("temp_session_id", OSD.FromUUID(groupID));
624 bodyMap.Add("success", OSD.FromBoolean(true));
625 bodyMap.Add("session_info", sessionMap);
626  
627 IEventQueue queue = remoteClient.Scene.RequestModuleInterface<IEventQueue>();
628  
629 if (queue != null)
630 {
631 queue.Enqueue(queue.BuildEvent("ChatterBoxSessionStartReply", bodyMap), remoteClient.AgentId);
632 }
633 }
634  
635 private void DebugGridInstantMessage(GridInstantMessage im)
636 {
637 // Don't log any normal IMs (privacy!)
638 if (m_debugEnabled && im.dialog != (byte)InstantMessageDialog.MessageFromAgent)
639 {
640 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromGroup({0})", im.fromGroup ? "True" : "False");
641 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: Dialog({0})", (InstantMessageDialog)im.dialog);
642 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentID({0})", im.fromAgentID);
643 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: fromAgentName({0})", im.fromAgentName);
644 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: imSessionID({0})", im.imSessionID);
645 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: message({0})", im.message);
646 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: offline({0})", im.offline);
647 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: toAgentID({0})", im.toAgentID);
648 m_log.DebugFormat("[GROUPS-MESSAGING]: IM: binaryBucket({0})", OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, "BinaryBucket"));
649 }
650 }
651  
652 #region Client Tools
653  
654 /// <summary>
655 /// Try to find an active IClientAPI reference for agentID giving preference to root connections
656 /// </summary>
657 private IClientAPI GetActiveClient(UUID agentID)
658 {
659 if (m_debugEnabled)
660 m_log.DebugFormat("[GROUPS-MESSAGING]: Looking for local client {0}", agentID);
661  
662 IClientAPI child = null;
663  
664 // Try root avatar first
665 foreach (Scene scene in m_sceneList)
666 {
667 ScenePresence sp = scene.GetScenePresence(agentID);
668 if (sp != null)
669 {
670 if (!sp.IsChildAgent)
671 {
672 if (m_debugEnabled)
673 m_log.DebugFormat("[GROUPS-MESSAGING]: Found root agent for client : {0}", sp.ControllingClient.Name);
674  
675 return sp.ControllingClient;
676 }
677 else
678 {
679 if (m_debugEnabled)
680 m_log.DebugFormat("[GROUPS-MESSAGING]: Found child agent for client : {0}", sp.ControllingClient.Name);
681  
682 child = sp.ControllingClient;
683 }
684 }
685 }
686  
687 // If we didn't find a root, then just return whichever child we found, or null if none
688 if (child == null)
689 {
690 if (m_debugEnabled)
691 m_log.DebugFormat("[GROUPS-MESSAGING]: Could not find local client for agent : {0}", agentID);
692 }
693 else
694 {
695 if (m_debugEnabled)
696 m_log.DebugFormat("[GROUPS-MESSAGING]: Returning child agent for client : {0}", child.Name);
697 }
698  
699 return child;
700 }
701  
702 #endregion
703 }
704 }