OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
GroupsMessagingModule.cs
Go to the documentation of this file.
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;
42 
43 namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
44 {
45  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsMessagingModule")]
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 
64  private bool m_messageOnlineAgentsOnly;
65 
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 
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
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 
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 }
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void SendMessageToGroup(GridInstantMessage im, UUID groupID)
Send a message to each member of a group whose chat session is active.
void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
OpenMetaverse.StructuredData.OSDMap OSDMap
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Interactive OpenSim region server
Definition: OpenSim.cs:55
void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void SendMessageToGroup(GridInstantMessage im, UUID groupID, UUID sendingAgentForGroupCalls, Func< GroupMembersData, bool > sendCondition)
Send a message to all the members of a group that fulfill a condition.
bool StartGroupChatSession(UUID agentID, UUID groupID)
Not really needed, but does confirm that the group exists.
OpenSim.Services.Interfaces.PresenceInfo PresenceInfo
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...