aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs1064
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs60
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs57
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs70
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs78
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs381
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs56
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs64
-rw-r--r--OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml8
-rw-r--r--OpenSim/Region/Framework/Interfaces/IPresenceModule.cs10
10 files changed, 289 insertions, 1559 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index d6a82ef..f5d30b7 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -44,1088 +44,50 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
44 44
45namespace OpenSim.Region.CoreModules.Avatar.Friends 45namespace OpenSim.Region.CoreModules.Avatar.Friends
46{ 46{
47 /* 47 public class FriendsModule : ISharedRegionModule, IFriendsModule
48 This module handles adding/removing friends, and the the presence
49 notification process for login/logoff of friends.
50
51 The presence notification works as follows:
52 - After the user initially connects to a region (so we now have a UDP
53 connection to work with), this module fetches the friends of user
54 (those are cached), their on-/offline status, and info about the
55 region they are in from the MessageServer.
56 - (*) It then informs the user about the on-/offline status of her friends.
57 - It then informs all online friends currently on this region-server about
58 user's new online status (this will save some network traffic, as local
59 messages don't have to be transferred inter-region, and it will be all
60 that has to be done in Standalone Mode).
61 - For the rest of the online friends (those not on this region-server),
62 this module uses the provided region-information to map users to
63 regions, and sends one notification to every region containing the
64 friends to inform on that server.
65 - The region-server will handle that in the following way:
66 - If it finds the friend, it informs her about the user being online.
67 - If it doesn't find the friend (maybe she TPed away in the meantime),
68 it stores that information.
69 - After it processed all friends, it returns the list of friends it
70 couldn't find.
71 - If this list isn't empty, the FriendsModule re-requests information
72 about those online friends that have been missed and starts at (*)
73 again until all friends have been found, or until it tried 3 times
74 (to prevent endless loops due to some uncaught error).
75
76 NOTE: Online/Offline notifications don't need to be sent on region change.
77
78 We implement two XMLRpc handlers here, handling all the inter-region things
79 we have to handle:
80 - On-/Offline-Notifications (bulk)
81 - Terminate Friendship messages (single)
82 */
83
84 public class FriendsModule : IRegionModule, IFriendsModule
85 { 48 {
86 private class Transaction 49 public void Initialise(IConfigSource config)
87 { 50 {
88 public UUID agentID;
89 public string agentName;
90 public uint count;
91
92 public Transaction(UUID agentID, string agentName)
93 {
94 this.agentID = agentID;
95 this.agentName = agentName;
96 this.count = 1;
97 }
98 }
99
100 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
101
102 private Cache m_friendLists = new Cache(CacheFlags.AllowUpdate);
103
104 private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>();
105
106 private Dictionary<UUID, UUID> m_pendingCallingcardRequests = new Dictionary<UUID,UUID>();
107
108 private Scene m_initialScene; // saves a lookup if we don't have a specific scene
109 private Dictionary<ulong, Scene> m_scenes = new Dictionary<ulong,Scene>();
110 private IMessageTransferModule m_TransferModule = null;
111
112 private IGridService m_gridServices = null;
113
114 #region IRegionModule Members
115
116 public void Initialise(Scene scene, IConfigSource config)
117 {
118 lock (m_scenes)
119 {
120 if (m_scenes.Count == 0)
121 {
122 MainServer.Instance.AddXmlRPCHandler("presence_update_bulk", processPresenceUpdateBulk);
123 MainServer.Instance.AddXmlRPCHandler("terminate_friend", processTerminateFriend);
124 m_friendLists.DefaultTTL = new TimeSpan(1, 0, 0); // store entries for one hour max
125 m_initialScene = scene;
126 }
127
128 if (!m_scenes.ContainsKey(scene.RegionInfo.RegionHandle))
129 m_scenes[scene.RegionInfo.RegionHandle] = scene;
130 }
131
132 scene.RegisterModuleInterface<IFriendsModule>(this);
133
134 scene.EventManager.OnNewClient += OnNewClient;
135 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
136 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
137 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
138 scene.EventManager.OnClientClosed += ClientClosed;
139 } 51 }
140 52
141 public void PostInitialise() 53 public void PostInitialise()
142 { 54 {
143 if (m_scenes.Count > 0)
144 {
145 m_TransferModule = m_initialScene.RequestModuleInterface<IMessageTransferModule>();
146 m_gridServices = m_initialScene.GridService;
147 }
148 if (m_TransferModule == null)
149 m_log.Error("[FRIENDS]: Unable to find a message transfer module, friendship offers will not work");
150 }
151
152 public void Close()
153 {
154 }
155
156 public string Name
157 {
158 get { return "FriendsModule"; }
159 }
160
161 public bool IsSharedModule
162 {
163 get { return true; }
164 }
165
166 #endregion
167
168 #region IInterregionFriendsComms
169
170 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
171 {
172 List<UUID> tpdAway = new List<UUID>();
173
174 // destRegionHandle is a region on another server
175 uint x = 0, y = 0;
176 Utils.LongToUInts(destRegionHandle, out x, out y);
177 GridRegion info = m_gridServices.GetRegionByPosition(m_initialScene.RegionInfo.ScopeID, (int)x, (int)y);
178 if (info != null)
179 {
180 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk";
181
182 Hashtable reqParams = new Hashtable();
183 reqParams["agentID"] = agentId.ToString();
184 reqParams["agentOnline"] = online;
185 int count = 0;
186 foreach (UUID uuid in friends)
187 {
188 reqParams["friendID_" + count++] = uuid.ToString();
189 }
190 reqParams["friendCount"] = count;
191
192 IList parameters = new ArrayList();
193 parameters.Add(reqParams);
194 try
195 {
196 XmlRpcRequest request = new XmlRpcRequest("presence_update_bulk", parameters);
197 XmlRpcResponse response = request.Send(httpServer, 5000);
198 Hashtable respData = (Hashtable)response.Value;
199
200 count = (int)respData["friendCount"];
201 for (int i = 0; i < count; ++i)
202 {
203 UUID uuid;
204 if (UUID.TryParse((string)respData["friendID_" + i], out uuid)) tpdAway.Add(uuid);
205 }
206 }
207 catch (WebException e)
208 {
209 // Ignore connect failures, simulators come and go
210 //
211 if (!e.Message.Contains("ConnectFailure"))
212 {
213 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
214 }
215 }
216 catch (Exception e)
217 {
218 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
219 }
220 }
221 else m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}???", destRegionHandle);
222
223 return tpdAway;
224 }
225
226 public bool TriggerTerminateFriend(ulong destRegionHandle, UUID agentID, UUID exFriendID)
227 {
228 // destRegionHandle is a region on another server
229 uint x = 0, y = 0;
230 Utils.LongToUInts(destRegionHandle, out x, out y);
231 GridRegion info = m_gridServices.GetRegionByPosition(m_initialScene.RegionInfo.ScopeID, (int)x, (int)y);
232 if (info == null)
233 {
234 m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}", destRegionHandle);
235 return false; // region not found???
236 }
237
238 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk";
239
240 Hashtable reqParams = new Hashtable();
241 reqParams["agentID"] = agentID.ToString();
242 reqParams["friendID"] = exFriendID.ToString();
243
244 IList parameters = new ArrayList();
245 parameters.Add(reqParams);
246 try
247 {
248 XmlRpcRequest request = new XmlRpcRequest("terminate_friend", parameters);
249 XmlRpcResponse response = request.Send(httpServer, 5000);
250 Hashtable respData = (Hashtable)response.Value;
251
252 return (bool)respData["success"];
253 }
254 catch (Exception e)
255 {
256 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
257 return false;
258 }
259 }
260
261 #endregion
262
263 #region Incoming XMLRPC messages
264 /// <summary>
265 /// Receive presence information changes about clients in other regions.
266 /// </summary>
267 /// <param name="req"></param>
268 /// <returns></returns>
269 public XmlRpcResponse processPresenceUpdateBulk(XmlRpcRequest req, IPEndPoint remoteClient)
270 {
271 Hashtable requestData = (Hashtable)req.Params[0];
272
273 List<UUID> friendsNotHere = new List<UUID>();
274
275 // this is called with the expectation that all the friends in the request are on this region-server.
276 // But as some time passed since we checked (on the other region-server, via the MessagingServer),
277 // some of the friends might have teleported away.
278 // Actually, even now, between this line and the sending below, some people could TP away. So,
279 // we'll have to lock the m_rootAgents list for the duration to prevent/delay that.
280 lock (m_rootAgents)
281 {
282 List<ScenePresence> friendsHere = new List<ScenePresence>();
283
284 try
285 {
286 UUID agentID = new UUID((string)requestData["agentID"]);
287 bool agentOnline = (bool)requestData["agentOnline"];
288 int count = (int)requestData["friendCount"];
289 for (int i = 0; i < count; ++i)
290 {
291 UUID uuid;
292 if (UUID.TryParse((string)requestData["friendID_" + i], out uuid))
293 {
294 if (m_rootAgents.ContainsKey(uuid)) friendsHere.Add(GetRootPresenceFromAgentID(uuid));
295 else friendsNotHere.Add(uuid);
296 }
297 }
298
299 // now send, as long as they are still here...
300 UUID[] agentUUID = new UUID[] { agentID };
301 if (agentOnline)
302 {
303 foreach (ScenePresence agent in friendsHere)
304 {
305 agent.ControllingClient.SendAgentOnline(agentUUID);
306 }
307 }
308 else
309 {
310 foreach (ScenePresence agent in friendsHere)
311 {
312 agent.ControllingClient.SendAgentOffline(agentUUID);
313 }
314 }
315 }
316 catch(Exception e)
317 {
318 m_log.Warn("[FRIENDS]: Got exception while parsing presence_update_bulk request:", e);
319 }
320 }
321
322 // no need to lock anymore; if TPs happen now, worst case is that we have an additional agent in this region,
323 // which should be caught on the next iteration...
324 Hashtable result = new Hashtable();
325 int idx = 0;
326 foreach (UUID uuid in friendsNotHere)
327 {
328 result["friendID_" + idx++] = uuid.ToString();
329 }
330 result["friendCount"] = idx;
331
332 XmlRpcResponse response = new XmlRpcResponse();
333 response.Value = result;
334
335 return response;
336 }
337
338 public XmlRpcResponse processTerminateFriend(XmlRpcRequest req, IPEndPoint remoteClient)
339 {
340 Hashtable requestData = (Hashtable)req.Params[0];
341
342 bool success = false;
343
344 UUID agentID;
345 UUID friendID;
346 if (requestData.ContainsKey("agentID") && UUID.TryParse((string)requestData["agentID"], out agentID) &&
347 requestData.ContainsKey("friendID") && UUID.TryParse((string)requestData["friendID"], out friendID))
348 {
349 // try to find it and if it is there, prevent it to vanish before we sent the message
350 lock (m_rootAgents)
351 {
352 if (m_rootAgents.ContainsKey(agentID))
353 {
354 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", friendID, agentID);
355 GetRootPresenceFromAgentID(agentID).ControllingClient.SendTerminateFriend(friendID);
356 success = true;
357 }
358 }
359 }
360
361 // return whether we were successful
362 Hashtable result = new Hashtable();
363 result["success"] = success;
364
365 XmlRpcResponse response = new XmlRpcResponse();
366 response.Value = result;
367 return response;
368 }
369
370 #endregion
371
372 #region Scene events
373
374 private void OnNewClient(IClientAPI client)
375 {
376 // All friends establishment protocol goes over instant message
377 // There's no way to send a message from the sim
378 // to a user to 'add a friend' without causing dialog box spam
379
380 // Subscribe to instant messages
381 client.OnInstantMessage += OnInstantMessage;
382
383 // Friend list management
384 client.OnApproveFriendRequest += OnApproveFriendRequest;
385 client.OnDenyFriendRequest += OnDenyFriendRequest;
386 client.OnTerminateFriendship += OnTerminateFriendship;
387
388 // ... calling card handling...
389 client.OnOfferCallingCard += OnOfferCallingCard;
390 client.OnAcceptCallingCard += OnAcceptCallingCard;
391 client.OnDeclineCallingCard += OnDeclineCallingCard;
392
393 // we need this one exactly once per agent session (see comments in the handler below)
394 client.OnEconomyDataRequest += OnEconomyDataRequest;
395
396 // if it leaves, we want to know, too
397 client.OnLogout += OnLogout;
398 client.OnGrantUserRights += GrantUserFriendRights;
399
400 }
401
402 private void ClientClosed(UUID AgentId, Scene scene)
403 {
404 // agent's client was closed. As we handle logout in OnLogout, this here has only to handle
405 // TPing away (root agent is closed) or TPing/crossing in a region far enough away (client
406 // agent is closed).
407 // NOTE: In general, this doesn't mean that the agent logged out, just that it isn't around
408 // in one of the regions here anymore.
409 lock (m_rootAgents)
410 {
411 if (m_rootAgents.ContainsKey(AgentId))
412 {
413 m_rootAgents.Remove(AgentId);
414 }
415 }
416 }
417
418 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID)
419 {
420 lock (m_rootAgents)
421 {
422 m_rootAgents[avatar.UUID] = avatar.RegionHandle;
423 // Claim User! my user! Mine mine mine!
424 }
425 }
426
427 private void MakeChildAgent(ScenePresence avatar)
428 {
429 lock (m_rootAgents)
430 {
431 if (m_rootAgents.ContainsKey(avatar.UUID))
432 {
433 // only delete if the region matches. As this is a shared module, the avatar could be
434 // root agent in another region on this server.
435 if (m_rootAgents[avatar.UUID] == avatar.RegionHandle)
436 {
437 m_rootAgents.Remove(avatar.UUID);
438// m_log.Debug("[FRIEND]: Removing " + avatar.Firstname + " " + avatar.Lastname + " as a root agent");
439 }
440 }
441 }
442 }
443 #endregion
444
445 private ScenePresence GetRootPresenceFromAgentID(UUID AgentID)
446 {
447 ScenePresence returnAgent = null;
448 lock (m_scenes)
449 {
450 ScenePresence queryagent = null;
451 foreach (Scene scene in m_scenes.Values)
452 {
453 queryagent = scene.GetScenePresence(AgentID);
454 if (queryagent != null)
455 {
456 if (!queryagent.IsChildAgent)
457 {
458 returnAgent = queryagent;
459 break;
460 }
461 }
462 }
463 }
464 return returnAgent;
465 }
466
467 private ScenePresence GetAnyPresenceFromAgentID(UUID AgentID)
468 {
469 ScenePresence returnAgent = null;
470 lock (m_scenes)
471 {
472 ScenePresence queryagent = null;
473 foreach (Scene scene in m_scenes.Values)
474 {
475 queryagent = scene.GetScenePresence(AgentID);
476 if (queryagent != null)
477 {
478 returnAgent = queryagent;
479 break;
480 }
481 }
482 }
483 return returnAgent;
484 }
485
486 public void OfferFriendship(UUID fromUserId, IClientAPI toUserClient, string offerMessage)
487 {
488 CachedUserInfo userInfo = m_initialScene.CommsManager.UserProfileCacheService.GetUserDetails(fromUserId);
489
490 if (userInfo != null)
491 {
492 GridInstantMessage msg = new GridInstantMessage(
493 toUserClient.Scene, fromUserId, userInfo.UserProfile.Name, toUserClient.AgentId,
494 (byte)InstantMessageDialog.FriendshipOffered, offerMessage, false, Vector3.Zero);
495
496 FriendshipOffered(msg);
497 }
498 else
499 {
500 m_log.ErrorFormat("[FRIENDS]: No user found for id {0} in OfferFriendship()", fromUserId);
501 }
502 }
503
504 #region FriendRequestHandling
505
506 private void OnInstantMessage(IClientAPI client, GridInstantMessage im)
507 {
508 // Friend Requests go by Instant Message.. using the dialog param
509 // https://wiki.secondlife.com/wiki/ImprovedInstantMessage
510
511 if (im.dialog == (byte)InstantMessageDialog.FriendshipOffered) // 38
512 {
513 // fromAgentName is the *destination* name (the friend we offer friendship to)
514 ScenePresence initiator = GetAnyPresenceFromAgentID(new UUID(im.fromAgentID));
515 im.fromAgentName = initiator != null ? initiator.Name : "(hippo)";
516
517 FriendshipOffered(im);
518 }
519 else if (im.dialog == (byte)InstantMessageDialog.FriendshipAccepted) // 39
520 {
521 FriendshipAccepted(client, im);
522 }
523 else if (im.dialog == (byte)InstantMessageDialog.FriendshipDeclined) // 40
524 {
525 FriendshipDeclined(client, im);
526 }
527 }
528
529 /// <summary>
530 /// Invoked when a user offers a friendship.
531 /// </summary>
532 ///
533 /// <param name="im"></param>
534 /// <param name="client"></param>
535 private void FriendshipOffered(GridInstantMessage im)
536 {
537 // this is triggered by the initiating agent:
538 // A local agent offers friendship to some possibly remote friend.
539 // A IM is triggered, processed here and sent to the friend (possibly in a remote region).
540
541 m_log.DebugFormat("[FRIEND]: Offer(38) - From: {0}, FromName: {1} To: {2}, Session: {3}, Message: {4}, Offline {5}",
542 im.fromAgentID, im.fromAgentName, im.toAgentID, im.imSessionID, im.message, im.offline);
543
544 // 1.20 protocol sends an UUID in the message field, instead of the friendship offer text.
545 // For interoperability, we have to clear that
546 if (Util.isUUID(im.message)) im.message = "";
547
548 // be sneeky and use the initiator-UUID as transactionID. This means we can be stateless.
549 // we have to look up the agent name on friendship-approval, though.
550 im.imSessionID = im.fromAgentID;
551
552 if (m_TransferModule != null)
553 {
554 // Send it to whoever is the destination.
555 // If new friend is local, it will send an IM to the viewer.
556 // If new friend is remote, it will cause a OnGridInstantMessage on the remote server
557 m_TransferModule.SendInstantMessage(
558 im,
559 delegate(bool success)
560 {
561 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
562 }
563 );
564 }
565 }
566
567 /// <summary>
568 /// Invoked when a user accepts a friendship offer.
569 /// </summary>
570 /// <param name="im"></param>
571 /// <param name="client"></param>
572 private void FriendshipAccepted(IClientAPI client, GridInstantMessage im)
573 {
574 m_log.DebugFormat("[FRIEND]: 39 - from client {0}, agent {2} {3}, imsession {4} to {5}: {6} (dialog {7})",
575 client.AgentId, im.fromAgentID, im.fromAgentName, im.imSessionID, im.toAgentID, im.message, im.dialog);
576 }
577
578 /// <summary>
579 /// Invoked when a user declines a friendship offer.
580 /// </summary>
581 /// May not currently be used - see OnDenyFriendRequest() instead
582 /// <param name="im"></param>
583 /// <param name="client"></param>
584 private void FriendshipDeclined(IClientAPI client, GridInstantMessage im)
585 {
586 UUID fromAgentID = new UUID(im.fromAgentID);
587 UUID toAgentID = new UUID(im.toAgentID);
588
589 // declining the friendship offer causes a type 40 IM being sent to the (possibly remote) initiator
590 // toAgentID is initiator, fromAgentID declined friendship
591 m_log.DebugFormat("[FRIEND]: 40 - from client {0}, agent {1} {2}, imsession {3} to {4}: {5} (dialog {6})",
592 client != null ? client.AgentId.ToString() : "<null>",
593 fromAgentID, im.fromAgentName, im.imSessionID, im.toAgentID, im.message, im.dialog);
594
595 // Send the decline to whoever is the destination.
596 GridInstantMessage msg
597 = new GridInstantMessage(
598 client.Scene, fromAgentID, client.Name, toAgentID,
599 im.dialog, im.message, im.offline != 0, im.Position);
600
601 // If new friend is local, it will send an IM to the viewer.
602 // If new friend is remote, it will cause a OnGridInstantMessage on the remote server
603 m_TransferModule.SendInstantMessage(msg,
604 delegate(bool success) {
605 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
606 }
607 );
608 }
609
610 private void OnGridInstantMessage(GridInstantMessage msg)
611 {
612 // This event won't be raised unless we have that agent,
613 // so we can depend on the above not trying to send
614 // via grid again
615 //m_log.DebugFormat("[FRIEND]: Got GridIM from {0}, to {1}, imSession {2}, message {3}, dialog {4}",
616 // msg.fromAgentID, msg.toAgentID, msg.imSessionID, msg.message, msg.dialog);
617
618 if (msg.dialog == (byte)InstantMessageDialog.FriendshipOffered ||
619 msg.dialog == (byte)InstantMessageDialog.FriendshipAccepted ||
620 msg.dialog == (byte)InstantMessageDialog.FriendshipDeclined)
621 {
622 // this should succeed as we *know* the root agent is here.
623 m_TransferModule.SendInstantMessage(msg,
624 delegate(bool success) {
625 //m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
626 }
627 );
628 }
629
630 if (msg.dialog == (byte)InstantMessageDialog.FriendshipAccepted)
631 {
632 // for accept friendship, we have to do a bit more
633 ApproveFriendship(new UUID(msg.fromAgentID), new UUID(msg.toAgentID), msg.fromAgentName);
634 }
635 }
636
637 private void ApproveFriendship(UUID fromAgentID, UUID toAgentID, string fromName)
638 {
639 m_log.DebugFormat("[FRIEND]: Approve friendship from {0} (ID: {1}) to {2}",
640 fromAgentID, fromName, toAgentID);
641
642 // a new friend was added in the initiator's and friend's data, so the cache entries are wrong now.
643 lock (m_friendLists)
644 {
645 m_friendLists.Invalidate(fromAgentID.ToString());
646 m_friendLists.Invalidate(toAgentID.ToString());
647 }
648
649 // now send presence update and add a calling card for the new friend
650
651 ScenePresence initiator = GetAnyPresenceFromAgentID(toAgentID);
652 if (initiator == null)
653 {
654 // quite wrong. Shouldn't happen.
655 m_log.WarnFormat("[FRIEND]: Coudn't find initiator of friend request {0}", toAgentID);
656 return;
657 }
658
659 m_log.DebugFormat("[FRIEND]: Tell {0} that {1} is online",
660 initiator.Name, fromName);
661 // tell initiator that friend is online
662 initiator.ControllingClient.SendAgentOnline(new UUID[] { fromAgentID });
663
664 // find the folder for the friend...
665 //InventoryFolderImpl folder =
666 // initiator.Scene.CommsManager.UserProfileCacheService.GetUserDetails(toAgentID).FindFolderForType((int)InventoryType.CallingCard);
667 IInventoryService invService = initiator.Scene.InventoryService;
668 InventoryFolderBase folder = invService.GetFolderForType(toAgentID, AssetType.CallingCard);
669 if (folder != null)
670 {
671 // ... and add the calling card
672 CreateCallingCard(initiator.ControllingClient, fromAgentID, folder.ID, fromName);
673 }
674 } 55 }
675 56
676 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 57 public void AddRegion(Scene scene)
677 { 58 {
678 m_log.DebugFormat("[FRIEND]: Got approve friendship from {0} {1}, agentID {2}, tid {3}",
679 client.Name, client.AgentId, agentID, friendID);
680
681 // store the new friend persistently for both avatars
682 m_initialScene.StoreAddFriendship(friendID, agentID, (uint) FriendRights.CanSeeOnline);
683
684 // The cache entries aren't valid anymore either, as we just added a friend to both sides.
685 lock (m_friendLists)
686 {
687 m_friendLists.Invalidate(agentID.ToString());
688 m_friendLists.Invalidate(friendID.ToString());
689 }
690
691 // if it's a local friend, we don't have to do the lookup
692 ScenePresence friendPresence = GetAnyPresenceFromAgentID(friendID);
693
694 if (friendPresence != null)
695 {
696 m_log.Debug("[FRIEND]: Local agent detected.");
697
698 // create calling card
699 CreateCallingCard(client, friendID, callingCardFolders[0], friendPresence.Name);
700
701 // local message means OnGridInstantMessage won't be triggered, so do the work here.
702 friendPresence.ControllingClient.SendInstantMessage(
703 new GridInstantMessage(client.Scene, agentID,
704 client.Name, friendID,
705 (byte)InstantMessageDialog.FriendshipAccepted,
706 agentID.ToString(), false, Vector3.Zero));
707 ApproveFriendship(agentID, friendID, client.Name);
708 }
709 else
710 {
711 m_log.Debug("[FRIEND]: Remote agent detected.");
712
713 // fetch the friend's name for the calling card.
714 CachedUserInfo info = m_initialScene.CommsManager.UserProfileCacheService.GetUserDetails(friendID);
715
716 // create calling card
717 CreateCallingCard(client, friendID, callingCardFolders[0],
718 info.UserProfile.FirstName + " " + info.UserProfile.SurName);
719
720 // Compose (remote) response to friend.
721 GridInstantMessage msg = new GridInstantMessage(client.Scene, agentID, client.Name, friendID,
722 (byte)InstantMessageDialog.FriendshipAccepted,
723 agentID.ToString(), false, Vector3.Zero);
724 if (m_TransferModule != null)
725 {
726 m_TransferModule.SendInstantMessage(msg,
727 delegate(bool success) {
728 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
729 }
730 );
731 }
732 }
733
734 // tell client that new friend is online
735 client.SendAgentOnline(new UUID[] { friendID });
736 } 59 }
737 60
738 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID friendID, List<UUID> callingCardFolders) 61 public void RegionLoaded(Scene scene)
739 { 62 {
740 m_log.DebugFormat("[FRIEND]: Got deny friendship from {0} {1}, agentID {2}, tid {3}",
741 client.Name, client.AgentId, agentID, friendID);
742
743 // Compose response to other agent.
744 GridInstantMessage msg = new GridInstantMessage(client.Scene, agentID, client.Name, friendID,
745 (byte)InstantMessageDialog.FriendshipDeclined,
746 agentID.ToString(), false, Vector3.Zero);
747 // send decline to initiator
748 if (m_TransferModule != null)
749 {
750 m_TransferModule.SendInstantMessage(msg,
751 delegate(bool success) {
752 m_log.DebugFormat("[FRIEND]: sending IM success = {0}", success);
753 }
754 );
755 }
756 } 63 }
757 64
758 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID) 65 public void RemoveRegion(Scene scene)
759 { 66 {
760 // client.AgentId == agentID!
761
762 // this removes the friends from the stored friendlists. After the next login, they will be gone...
763 m_initialScene.StoreRemoveFriendship(agentID, exfriendID);
764
765 // ... now tell the two involved clients that they aren't friends anymore.
766
767 // I don't know why we have to tell <agent>, as this was caused by her, but that's how it works in SL...
768 client.SendTerminateFriend(exfriendID);
769
770 // now send the friend, if online
771 ScenePresence presence = GetAnyPresenceFromAgentID(exfriendID);
772 if (presence != null)
773 {
774 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", agentID, exfriendID);
775 presence.ControllingClient.SendTerminateFriend(agentID);
776 }
777 else
778 {
779 // retry 3 times, in case the agent TPed from the last known region...
780 for (int retry = 0; retry < 3; ++retry)
781 {
782 // wasn't sent, so ex-friend wasn't around on this region-server. Fetch info and try to send
783 UserAgentData data = m_initialScene.CommsManager.UserService.GetAgentByUUID(exfriendID);
784
785 if (null == data)
786 break;
787
788 if (!data.AgentOnline)
789 {
790 m_log.DebugFormat("[FRIEND]: {0} is offline, so not sending TerminateFriend", exfriendID);
791 break; // if ex-friend isn't online, we don't need to send
792 }
793
794 m_log.DebugFormat("[FRIEND]: Sending remote terminate friend {0} to agent {1}@{2}",
795 agentID, exfriendID, data.Handle);
796
797 // try to send to foreign region, retry if it fails (friend TPed away, for example)
798 if (TriggerTerminateFriend(data.Handle, exfriendID, agentID)) break;
799 }
800 }
801
802 // clean up cache: FriendList is wrong now...
803 lock (m_friendLists)
804 {
805 m_friendLists.Invalidate(agentID.ToString());
806 m_friendLists.Invalidate(exfriendID.ToString());
807 }
808 } 67 }
809 68
810 #endregion 69 public void Close()
811
812 #region CallingCards
813
814 private void OnOfferCallingCard(IClientAPI client, UUID destID, UUID transactionID)
815 {
816 m_log.DebugFormat("[CALLING CARD]: got offer from {0} for {1}, transaction {2}",
817 client.AgentId, destID, transactionID);
818 // This might be slightly wrong. On a multi-region server, we might get the child-agent instead of the root-agent
819 // (or the root instead of the child)
820 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
821 if (destAgent == null)
822 {
823 client.SendAlertMessage("The person you have offered a card to can't be found anymore.");
824 return;
825 }
826
827 lock (m_pendingCallingcardRequests)
828 {
829 m_pendingCallingcardRequests[transactionID] = client.AgentId;
830 }
831 // inform the destination agent about the offer
832 destAgent.ControllingClient.SendOfferCallingCard(client.AgentId, transactionID);
833 }
834
835 private void CreateCallingCard(IClientAPI client, UUID creator, UUID folder, string name)
836 {
837 InventoryItemBase item = new InventoryItemBase();
838 item.AssetID = UUID.Zero;
839 item.AssetType = (int)AssetType.CallingCard;
840 item.BasePermissions = (uint)PermissionMask.Copy;
841 item.CreationDate = Util.UnixTimeSinceEpoch();
842 item.CreatorId = creator.ToString();
843 item.CurrentPermissions = item.BasePermissions;
844 item.Description = "";
845 item.EveryOnePermissions = (uint)PermissionMask.None;
846 item.Flags = 0;
847 item.Folder = folder;
848 item.GroupID = UUID.Zero;
849 item.GroupOwned = false;
850 item.ID = UUID.Random();
851 item.InvType = (int)InventoryType.CallingCard;
852 item.Name = name;
853 item.NextPermissions = item.EveryOnePermissions;
854 item.Owner = client.AgentId;
855 item.SalePrice = 10;
856 item.SaleType = (byte)SaleType.Not;
857 ((Scene)client.Scene).AddInventoryItem(client, item);
858 }
859
860 private void OnAcceptCallingCard(IClientAPI client, UUID transactionID, UUID folderID)
861 {
862 m_log.DebugFormat("[CALLING CARD]: User {0} ({1} {2}) accepted tid {3}, folder {4}",
863 client.AgentId,
864 client.FirstName, client.LastName,
865 transactionID, folderID);
866 UUID destID;
867 lock (m_pendingCallingcardRequests)
868 {
869 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID))
870 {
871 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
872 client.Name);
873 return;
874 }
875 // else found pending calling card request with that transaction.
876 m_pendingCallingcardRequests.Remove(transactionID);
877 }
878
879
880 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
881 // inform sender of the card that destination declined the offer
882 if (destAgent != null) destAgent.ControllingClient.SendAcceptCallingCard(transactionID);
883
884 // put a calling card into the inventory of receiver
885 CreateCallingCard(client, destID, folderID, destAgent.Name);
886 }
887
888 private void OnDeclineCallingCard(IClientAPI client, UUID transactionID)
889 { 70 {
890 m_log.DebugFormat("[CALLING CARD]: User {0} (ID:{1}) declined card, tid {2}",
891 client.Name, client.AgentId, transactionID);
892 UUID destID;
893 lock (m_pendingCallingcardRequests)
894 {
895 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID))
896 {
897 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
898 client.Name);
899 return;
900 }
901 // else found pending calling card request with that transaction.
902 m_pendingCallingcardRequests.Remove(transactionID);
903 }
904
905 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
906 // inform sender of the card that destination declined the offer
907 if (destAgent != null) destAgent.ControllingClient.SendDeclineCallingCard(transactionID);
908 } 71 }
909 72
910 /// <summary> 73 public string Name
911 /// Send presence information about a client to other clients in both this region and others.
912 /// </summary>
913 /// <param name="client"></param>
914 /// <param name="friendList"></param>
915 /// <param name="iAmOnline"></param>
916 private void SendPresenceState(IClientAPI client, List<FriendListItem> friendList, bool iAmOnline)
917 { 74 {
918 //m_log.DebugFormat("[FRIEND]: {0} logged {1}; sending presence updates", client.Name, iAmOnline ? "in" : "out"); 75 get { return "FriendsModule"; }
919
920 if (friendList == null || friendList.Count == 0)
921 {
922 //m_log.DebugFormat("[FRIEND]: {0} doesn't have friends.", client.Name);
923 return; // nothing we can do if she doesn't have friends...
924 }
925
926 // collect sets of friendIDs; to send to (online and offline), and to receive from
927 // TODO: If we ever switch to .NET >= 3, replace those Lists with HashSets.
928 // I can't believe that we have Dictionaries, but no Sets, considering Java introduced them years ago...
929 List<UUID> friendIDsToSendTo = new List<UUID>();
930 List<UUID> candidateFriendIDsToReceive = new List<UUID>();
931
932 foreach (FriendListItem item in friendList)
933 {
934 if (((item.FriendListOwnerPerms | item.FriendPerms) & (uint)FriendRights.CanSeeOnline) != 0)
935 {
936 // friend is allowed to see my presence => add
937 if ((item.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0)
938 friendIDsToSendTo.Add(item.Friend);
939
940 if ((item.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0)
941 candidateFriendIDsToReceive.Add(item.Friend);
942 }
943 }
944
945 // we now have a list of "interesting" friends (which we have to find out on-/offline state for),
946 // friends we want to send our online state to (if *they* are online, too), and
947 // friends we want to receive online state for (currently unknown whether online or not)
948
949 // as this processing might take some time and friends might TP away, we try up to three times to
950 // reach them. Most of the time, we *will* reach them, and this loop won't loop
951 int retry = 0;
952 do
953 {
954 // build a list of friends to look up region-information and on-/offline-state for
955 List<UUID> friendIDsToLookup = new List<UUID>(friendIDsToSendTo);
956 foreach (UUID uuid in candidateFriendIDsToReceive)
957 {
958 if (!friendIDsToLookup.Contains(uuid)) friendIDsToLookup.Add(uuid);
959 }
960
961 m_log.DebugFormat(
962 "[FRIEND]: {0} to lookup, {1} to send to, {2} candidates to receive from for agent {3}",
963 friendIDsToLookup.Count, friendIDsToSendTo.Count, candidateFriendIDsToReceive.Count, client.Name);
964
965 // we have to fetch FriendRegionInfos, as the (cached) FriendListItems don't
966 // necessarily contain the correct online state...
967 Dictionary<UUID, FriendRegionInfo> friendRegions = m_initialScene.GetFriendRegionInfos(friendIDsToLookup);
968 m_log.DebugFormat(
969 "[FRIEND]: Found {0} regionInfos for {1} friends of {2}",
970 friendRegions.Count, friendIDsToLookup.Count, client.Name);
971
972 // argument for SendAgentOn/Offline; we shouldn't generate that repeatedly within loops.
973 UUID[] agentArr = new UUID[] { client.AgentId };
974
975 // first, send to friend presence state to me, if I'm online...
976 if (iAmOnline)
977 {
978 List<UUID> friendIDsToReceive = new List<UUID>();
979
980 for (int i = candidateFriendIDsToReceive.Count - 1; i >= 0; --i)
981 {
982 UUID uuid = candidateFriendIDsToReceive[i];
983 FriendRegionInfo info;
984 if (friendRegions.TryGetValue(uuid, out info) && info != null && info.isOnline)
985 {
986 friendIDsToReceive.Add(uuid);
987 }
988 }
989
990 m_log.DebugFormat(
991 "[FRIEND]: Sending {0} online friends to {1}", friendIDsToReceive.Count, client.Name);
992
993 if (friendIDsToReceive.Count > 0)
994 client.SendAgentOnline(friendIDsToReceive.ToArray());
995
996 // clear them for a possible second iteration; we don't have to repeat this
997 candidateFriendIDsToReceive.Clear();
998 }
999
1000 // now, send my presence state to my friends
1001 for (int i = friendIDsToSendTo.Count - 1; i >= 0; --i)
1002 {
1003 UUID uuid = friendIDsToSendTo[i];
1004 FriendRegionInfo info;
1005 if (friendRegions.TryGetValue(uuid, out info) && info != null && info.isOnline)
1006 {
1007 // any client is good enough, root or child...
1008 ScenePresence agent = GetAnyPresenceFromAgentID(uuid);
1009 if (agent != null)
1010 {
1011 //m_log.DebugFormat("[FRIEND]: Found local agent {0}", agent.Name);
1012
1013 // friend is online and on this server...
1014 if (iAmOnline) agent.ControllingClient.SendAgentOnline(agentArr);
1015 else agent.ControllingClient.SendAgentOffline(agentArr);
1016
1017 // done, remove it
1018 friendIDsToSendTo.RemoveAt(i);
1019 }
1020 }
1021 else
1022 {
1023 //m_log.DebugFormat("[FRIEND]: Friend {0} ({1}) is offline; not sending.", uuid, i);
1024
1025 // friend is offline => no need to try sending
1026 friendIDsToSendTo.RemoveAt(i);
1027 }
1028 }
1029
1030 m_log.DebugFormat("[FRIEND]: Have {0} friends to contact via inter-region comms.", friendIDsToSendTo.Count);
1031
1032 // we now have all the friends left that are online (we think), but not on this region-server
1033 if (friendIDsToSendTo.Count > 0)
1034 {
1035 // sort them into regions
1036 Dictionary<ulong, List<UUID>> friendsInRegion = new Dictionary<ulong,List<UUID>>();
1037 foreach (UUID uuid in friendIDsToSendTo)
1038 {
1039 ulong handle = friendRegions[uuid].regionHandle; // this can't fail as we filtered above already
1040 List<UUID> friends;
1041 if (!friendsInRegion.TryGetValue(handle, out friends))
1042 {
1043 friends = new List<UUID>();
1044 friendsInRegion[handle] = friends;
1045 }
1046 friends.Add(uuid);
1047 }
1048 m_log.DebugFormat("[FRIEND]: Found {0} regions to send to.", friendRegions.Count);
1049
1050 // clear uuids list and collect missed friends in it for the next retry
1051 friendIDsToSendTo.Clear();
1052
1053 // send bulk updates to the region
1054 foreach (KeyValuePair<ulong, List<UUID>> pair in friendsInRegion)
1055 {
1056 //m_log.DebugFormat("[FRIEND]: Inform {0} friends in region {1} that user {2} is {3}line",
1057 // pair.Value.Count, pair.Key, client.Name, iAmOnline ? "on" : "off");
1058
1059 friendIDsToSendTo.AddRange(InformFriendsInOtherRegion(client.AgentId, pair.Key, pair.Value, iAmOnline));
1060 }
1061 }
1062 // now we have in friendIDsToSendTo only the agents left that TPed away while we tried to contact them.
1063 // In most cases, it will be empty, and it won't loop here. But sometimes, we have to work harder and try again...
1064 }
1065 while (++retry < 3 && friendIDsToSendTo.Count > 0);
1066 } 76 }
1067 77
1068 private void OnEconomyDataRequest(UUID agentID) 78 public Type ReplaceableInterface
1069 { 79 {
1070 // KLUDGE: This is the only way I found to get a message (only) after login was completed and the 80 get { return null; }
1071 // client is connected enough to receive UDP packets).
1072 // This packet seems to be sent only once, just after connection was established to the first
1073 // region after login.
1074 // We use it here to trigger a presence update; the old update-on-login was never be heard by
1075 // the freshly logged in viewer, as it wasn't connected to the region at that time.
1076 // TODO: Feel free to replace this by a better solution if you find one.
1077
1078 // get the agent. This should work every time, as we just got a packet from it
1079 //ScenePresence agent = GetRootPresenceFromAgentID(agentID);
1080 // KLUDGE 2: As this is sent quite early, the avatar isn't here as root agent yet. So, we have to cheat a bit
1081 ScenePresence agent = GetAnyPresenceFromAgentID(agentID);
1082
1083 // just to be paranoid...
1084 if (agent == null)
1085 {
1086 m_log.ErrorFormat("[FRIEND]: Got a packet from agent {0} who can't be found anymore!?", agentID);
1087 return;
1088 }
1089
1090 List<FriendListItem> fl;
1091 lock (m_friendLists)
1092 {
1093 fl = (List<FriendListItem>)m_friendLists.Get(agent.ControllingClient.AgentId.ToString(),
1094 m_initialScene.GetFriendList);
1095 }
1096
1097 // tell everyone that we are online
1098 SendPresenceState(agent.ControllingClient, fl, true);
1099 } 81 }
1100 82
1101 private void OnLogout(IClientAPI remoteClient) 83 public void OfferFriendship(UUID fromUserId, IClientAPI toUserClient,
84 string offerMessage)
1102 { 85 {
1103 List<FriendListItem> fl;
1104 lock (m_friendLists)
1105 {
1106 fl = (List<FriendListItem>)m_friendLists.Get(remoteClient.AgentId.ToString(),
1107 m_initialScene.GetFriendList);
1108 }
1109
1110 // tell everyone that we are offline
1111 SendPresenceState(remoteClient, fl, false);
1112 }
1113 private void GrantUserFriendRights(IClientAPI remoteClient, UUID requester, UUID target, int rights)
1114 {
1115 ((Scene)remoteClient.Scene).CommsManager.UpdateUserFriendPerms(requester, target, (uint)rights);
1116 } 86 }
1117 87
1118 public List<FriendListItem> GetUserFriends(UUID agentID) 88 public List<FriendListItem> GetUserFriends(UUID agentID)
1119 { 89 {
1120 List<FriendListItem> fl; 90 return null;
1121 lock (m_friendLists)
1122 {
1123 fl = (List<FriendListItem>)m_friendLists.Get(agentID.ToString(),
1124 m_initialScene.GetFriendList);
1125 }
1126
1127 return fl;
1128 } 91 }
1129 } 92 }
1130 #endregion
1131} 93}
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs
index 9a68749..ab141eb 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/InstantMessageModule.cs
@@ -24,6 +24,7 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 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. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using System;
27using System.Collections.Generic; 28using System.Collections.Generic;
28using System.Reflection; 29using System.Reflection;
29using log4net; 30using log4net;
@@ -36,9 +37,10 @@ using OpenSim.Region.Framework.Scenes;
36 37
37namespace OpenSim.Region.CoreModules.Avatar.InstantMessage 38namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
38{ 39{
39 public class InstantMessageModule : IRegionModule 40 public class InstantMessageModule : ISharedRegionModule
40 { 41 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 42 private static readonly ILog m_log = LogManager.GetLogger(
43 MethodBase.GetCurrentMethod().DeclaringType);
42 44
43 /// <value> 45 /// <value>
44 /// Is this module enabled? 46 /// Is this module enabled?
@@ -51,7 +53,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
51 53
52 private IMessageTransferModule m_TransferModule = null; 54 private IMessageTransferModule m_TransferModule = null;
53 55
54 public void Initialise(Scene scene, IConfigSource config) 56 public void Initialise(IConfigSource config)
55 { 57 {
56 if (config.Configs["Messaging"] != null) 58 if (config.Configs["Messaging"] != null)
57 { 59 {
@@ -62,6 +64,12 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
62 } 64 }
63 65
64 m_enabled = true; 66 m_enabled = true;
67 }
68
69 public void AddRegion(Scene scene)
70 {
71 if (!m_enabled)
72 return;
65 73
66 lock (m_scenes) 74 lock (m_scenes)
67 { 75 {
@@ -74,6 +82,39 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
74 } 82 }
75 } 83 }
76 84
85 public void RegionLoaded(Scene scene)
86 {
87 if (!m_enabled)
88 return;
89
90 if (m_TransferModule == null)
91 {
92 m_TransferModule =
93 scene.RequestModuleInterface<IMessageTransferModule>();
94
95 if (m_TransferModule == null)
96 {
97 m_log.Error("[INSTANT MESSAGE]: No message transfer module, IM will not work!");
98 scene.EventManager.OnClientConnect -= OnClientConnect;
99 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
100
101 m_scenes.Clear();
102 m_enabled = false;
103 }
104 }
105 }
106
107 public void RemoveRegion(Scene scene)
108 {
109 if (!m_enabled)
110 return;
111
112 lock (m_scenes)
113 {
114 m_scenes.Remove(scene);
115 }
116 }
117
77 void OnClientConnect(IClientCore client) 118 void OnClientConnect(IClientCore client)
78 { 119 {
79 IClientIM clientIM; 120 IClientIM clientIM;
@@ -85,15 +126,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
85 126
86 public void PostInitialise() 127 public void PostInitialise()
87 { 128 {
88 if (!m_enabled)
89 return;
90
91 m_TransferModule =
92 m_scenes[0].RequestModuleInterface<IMessageTransferModule>();
93
94 if (m_TransferModule == null)
95 m_log.Error("[INSTANT MESSAGE]: No message transfer module, "+
96 "IM will not work!");
97 } 129 }
98 130
99 public void Close() 131 public void Close()
@@ -105,9 +137,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
105 get { return "InstantMessageModule"; } 137 get { return "InstantMessageModule"; }
106 } 138 }
107 139
108 public bool IsSharedModule 140 public Type ReplaceableInterface
109 { 141 {
110 get { return true; } 142 get { return null; }
111 } 143 }
112 144
113 #endregion 145 #endregion
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
index e5159b3..2d4b421 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
@@ -40,18 +40,17 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
40 40
41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage 41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
42{ 42{
43 public class MessageTransferModule : IRegionModule, IMessageTransferModule 43 public class MessageTransferModule : ISharedRegionModule, IMessageTransferModule
44 { 44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 46
47 // private bool m_Enabled = false; 47 private bool m_Enabled = false;
48 protected bool m_Gridmode = false;
49 protected List<Scene> m_Scenes = new List<Scene>(); 48 protected List<Scene> m_Scenes = new List<Scene>();
50 protected Dictionary<UUID, ulong> m_UserRegionMap = new Dictionary<UUID, ulong>(); 49 protected Dictionary<UUID, ulong> m_UserRegionMap = new Dictionary<UUID, ulong>();
51 50
52 public event UndeliveredMessage OnUndeliveredMessage; 51 public event UndeliveredMessage OnUndeliveredMessage;
53 52
54 public virtual void Initialise(Scene scene, IConfigSource config) 53 public virtual void Initialise(IConfigSource config)
55 { 54 {
56 IConfig cnf = config.Configs["Messaging"]; 55 IConfig cnf = config.Configs["Messaging"];
57 if (cnf != null && cnf.GetString( 56 if (cnf != null && cnf.GetString(
@@ -62,20 +61,16 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
62 return; 61 return;
63 } 62 }
64 63
65 cnf = config.Configs["Startup"]; 64 m_Enabled = true;
66 if (cnf != null) 65 }
67 m_Gridmode = cnf.GetBoolean("gridmode", false);
68 66
69 // m_Enabled = true; 67 public virtual void AddRegion(Scene scene)
68 {
69 if (!m_Enabled)
70 return;
70 71
71 lock (m_Scenes) 72 lock (m_Scenes)
72 { 73 {
73 if (m_Scenes.Count == 0)
74 {
75 MainServer.Instance.AddXmlRPCHandler(
76 "grid_instant_message", processXMLRPCGridInstantMessage);
77 }
78
79 m_log.Debug("[MESSAGE TRANSFER]: Message transfer module active"); 74 m_log.Debug("[MESSAGE TRANSFER]: Message transfer module active");
80 scene.RegisterModuleInterface<IMessageTransferModule>(this); 75 scene.RegisterModuleInterface<IMessageTransferModule>(this);
81 m_Scenes.Add(scene); 76 m_Scenes.Add(scene);
@@ -84,6 +79,26 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
84 79
85 public virtual void PostInitialise() 80 public virtual void PostInitialise()
86 { 81 {
82 if (!m_Enabled)
83 return;
84
85 MainServer.Instance.AddXmlRPCHandler(
86 "grid_instant_message", processXMLRPCGridInstantMessage);
87 }
88
89 public virtual void RegionLoaded(Scene scene)
90 {
91 }
92
93 public virtual void RemoveRegion(Scene scene)
94 {
95 if (!m_Enabled)
96 return;
97
98 lock(m_Scenes)
99 {
100 m_Scenes.Remove(scene);
101 }
87 } 102 }
88 103
89 public virtual void Close() 104 public virtual void Close()
@@ -95,9 +110,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
95 get { return "MessageTransferModule"; } 110 get { return "MessageTransferModule"; }
96 } 111 }
97 112
98 public virtual bool IsSharedModule 113 public virtual Type ReplaceableInterface
99 { 114 {
100 get { return true; } 115 get { return null; }
101 } 116 }
102 117
103 public virtual void SendInstantMessage(GridInstantMessage im, MessageResultNotification result) 118 public virtual void SendInstantMessage(GridInstantMessage im, MessageResultNotification result)
@@ -148,15 +163,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
148 } 163 }
149 } 164 }
150 165
151 if (m_Gridmode) 166 SendGridInstantMessageViaXMLRPC(im, result);
152 {
153 //m_log.DebugFormat("[INSTANT MESSAGE]: Delivering via grid");
154 // Still here, try send via Grid
155 SendGridInstantMessageViaXMLRPC(im, result);
156 return;
157 }
158
159 HandleUndeliveredMessage(im, result);
160 167
161 return; 168 return;
162 } 169 }
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs
index 2d4a635..24cbaeb 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs
@@ -37,9 +37,9 @@ using OpenSim.Framework.Client;
37using OpenSim.Region.Framework.Interfaces; 37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Region.Framework.Scenes; 38using OpenSim.Region.Framework.Scenes;
39 39
40namespace OpenSim.Region.CoreModules.Avatar.MuteList 40namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
41{ 41{
42 public class MuteListModule : IRegionModule 42 public class MuteListModule : ISharedRegionModule
43 { 43 {
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 45
@@ -47,11 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.MuteList
47 private List<Scene> m_SceneList = new List<Scene>(); 47 private List<Scene> m_SceneList = new List<Scene>();
48 private string m_RestURL = String.Empty; 48 private string m_RestURL = String.Empty;
49 49
50 public void Initialise(Scene scene, IConfigSource config) 50 public void Initialise(IConfigSource config)
51 { 51 {
52 if (!enabled)
53 return;
54
55 IConfig cnf = config.Configs["Messaging"]; 52 IConfig cnf = config.Configs["Messaging"];
56 if (cnf == null) 53 if (cnf == null)
57 { 54 {
@@ -59,39 +56,53 @@ namespace OpenSim.Region.CoreModules.Avatar.MuteList
59 return; 56 return;
60 } 57 }
61 58
62 if (cnf != null && cnf.GetString( 59 if (cnf != null && cnf.GetString("MuteListModule", "None") !=
63 "MuteListModule", "None") !=
64 "MuteListModule") 60 "MuteListModule")
65 { 61 {
66 enabled = false; 62 enabled = false;
67 return; 63 return;
68 } 64 }
69 65
66 m_RestURL = cnf.GetString("MuteListURL", "");
67 if (m_RestURL == "")
68 {
69 m_log.Error("[MUTE LIST] Module was enabled, but no URL is given, disabling");
70 enabled = false;
71 return;
72 }
73 }
74
75 public void AddRegion(Scene scene)
76 {
77 if (!enabled)
78 return;
79
70 lock (m_SceneList) 80 lock (m_SceneList)
71 { 81 {
72 if (m_SceneList.Count == 0) 82 m_SceneList.Add(scene);
73 {
74 m_RestURL = cnf.GetString("MuteListURL", "");
75 if (m_RestURL == "")
76 {
77 m_log.Error("[MUTE LIST] Module was enabled, but no URL is given, disabling");
78 enabled = false;
79 return;
80 }
81 }
82 if (!m_SceneList.Contains(scene))
83 m_SceneList.Add(scene);
84 83
85 scene.EventManager.OnNewClient += OnNewClient; 84 scene.EventManager.OnNewClient += OnNewClient;
86 } 85 }
87 } 86 }
88 87
89 public void PostInitialise() 88 public void RegionLoaded(Scene scene)
89 {
90 }
91
92 public void RemoveRegion(Scene scene)
90 { 93 {
91 if (!enabled) 94 if (!enabled)
92 return; 95 return;
93 96
94 if (m_SceneList.Count == 0) 97 lock (m_SceneList)
98 {
99 m_SceneList.Remove(scene);
100 }
101 }
102
103 public void PostInitialise()
104 {
105 if (!enabled)
95 return; 106 return;
96 107
97 m_log.Debug("[MUTE LIST] Mute list enabled"); 108 m_log.Debug("[MUTE LIST] Mute list enabled");
@@ -102,26 +113,15 @@ namespace OpenSim.Region.CoreModules.Avatar.MuteList
102 get { return "MuteListModule"; } 113 get { return "MuteListModule"; }
103 } 114 }
104 115
105 public bool IsSharedModule 116 public Type ReplaceableInterface
106 { 117 {
107 get { return true; } 118 get { return null; }
108 } 119 }
109 120
110 public void Close() 121 public void Close()
111 { 122 {
112 } 123 }
113 124
114// private IClientAPI FindClient(UUID agentID)
115// {
116// foreach (Scene s in m_SceneList)
117// {
118// ScenePresence presence = s.GetScenePresence(agentID);
119// if (presence != null && !presence.IsChildAgent)
120// return presence.ControllingClient;
121// }
122// return null;
123// }
124
125 private void OnNewClient(IClientAPI client) 125 private void OnNewClient(IClientAPI client)
126 { 126 {
127 client.OnMuteListRequest += OnMuteListRequest; 127 client.OnMuteListRequest += OnMuteListRequest;
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
index 1614b70..a835c52 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
@@ -40,78 +40,88 @@ using OpenSim.Region.Framework.Scenes;
40 40
41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage 41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
42{ 42{
43 public class OfflineMessageModule : IRegionModule 43 public class OfflineMessageModule : ISharedRegionModule
44 { 44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 46
47 private bool enabled = true; 47 private bool enabled = true;
48 private List<Scene> m_SceneList = new List<Scene>(); 48 private List<Scene> m_SceneList = new List<Scene>();
49 private string m_RestURL = String.Empty; 49 private string m_RestURL = String.Empty;
50 IMessageTransferModule m_TransferModule = null;
50 51
51 public void Initialise(Scene scene, IConfigSource config) 52 public void Initialise(IConfigSource config)
52 { 53 {
53 if (!enabled)
54 return;
55
56 IConfig cnf = config.Configs["Messaging"]; 54 IConfig cnf = config.Configs["Messaging"];
57 if (cnf == null) 55 if (cnf == null)
58 { 56 {
59 enabled = false; 57 enabled = false;
60 return; 58 return;
61 } 59 }
62 if (cnf != null && cnf.GetString( 60 if (cnf != null && cnf.GetString("OfflineMessageModule", "None") !=
63 "OfflineMessageModule", "None") !=
64 "OfflineMessageModule") 61 "OfflineMessageModule")
65 { 62 {
66 enabled = false; 63 enabled = false;
67 return; 64 return;
68 } 65 }
69 66
67 m_RestURL = cnf.GetString("OfflineMessageURL", "");
68 if (m_RestURL == "")
69 {
70 m_log.Error("[OFFLINE MESSAGING] Module was enabled, but no URL is given, disabling");
71 enabled = false;
72 return;
73 }
74 }
75
76 public void AddRegion(Scene scene)
77 {
78 if (!enabled)
79 return;
80
70 lock (m_SceneList) 81 lock (m_SceneList)
71 { 82 {
72 if (m_SceneList.Count == 0) 83 m_SceneList.Add(scene);
73 {
74 m_RestURL = cnf.GetString("OfflineMessageURL", "");
75 if (m_RestURL == "")
76 {
77 m_log.Error("[OFFLINE MESSAGING] Module was enabled, but no URL is given, disabling");
78 enabled = false;
79 return;
80 }
81 }
82 if (!m_SceneList.Contains(scene))
83 m_SceneList.Add(scene);
84 84
85 scene.EventManager.OnNewClient += OnNewClient; 85 scene.EventManager.OnNewClient += OnNewClient;
86 } 86 }
87 } 87 }
88 88
89 public void PostInitialise() 89 public void RegionLoaded(Scene scene)
90 { 90 {
91 if (!enabled) 91 if (!enabled)
92 return; 92 return;
93 93
94 if (m_SceneList.Count == 0) 94 if (m_TransferModule == null)
95 return;
96
97 IMessageTransferModule trans = m_SceneList[0].RequestModuleInterface<IMessageTransferModule>();
98 if (trans == null)
99 { 95 {
100 enabled = false; 96 m_TransferModule = scene.RequestModuleInterface<IMessageTransferModule>();
101 97 if (m_TransferModule == null)
102 lock (m_SceneList)
103 { 98 {
104 foreach (Scene s in m_SceneList) 99 scene.EventManager.OnNewClient -= OnNewClient;
105 s.EventManager.OnNewClient -= OnNewClient;
106 100
101 enabled = false;
107 m_SceneList.Clear(); 102 m_SceneList.Clear();
103
104 m_log.Error("[OFFLINE MESSAGING] No message transfer module is enabled. Diabling offline messages");
108 } 105 }
106 m_TransferModule.OnUndeliveredMessage += UndeliveredMessage;
107 }
108 }
109 109
110 m_log.Error("[OFFLINE MESSAGING] No message transfer module is enabled. Diabling offline messages"); 110 public void RemoveRegion(Scene scene)
111 {
112 if (!enabled)
111 return; 113 return;
114
115 lock (m_SceneList)
116 {
117 m_SceneList.Remove(scene);
112 } 118 }
119 }
113 120
114 trans.OnUndeliveredMessage += UndeliveredMessage; 121 public void PostInitialise()
122 {
123 if (!enabled)
124 return;
115 125
116 m_log.Debug("[OFFLINE MESSAGING] Offline messages enabled"); 126 m_log.Debug("[OFFLINE MESSAGING] Offline messages enabled");
117 } 127 }
@@ -121,9 +131,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
121 get { return "OfflineMessageModule"; } 131 get { return "OfflineMessageModule"; }
122 } 132 }
123 133
124 public bool IsSharedModule 134 public Type ReplaceableInterface
125 { 135 {
126 get { return true; } 136 get { return null; }
127 } 137 }
128 138
129 public void Close() 139 public void Close()
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs
index f5ab454..267a90a 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs
@@ -24,6 +24,7 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 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. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using System;
27using System.Collections; 28using System.Collections;
28using System.Collections.Generic; 29using System.Collections.Generic;
29using System.Net; 30using System.Net;
@@ -39,404 +40,54 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion;
39 40
40namespace OpenSim.Region.CoreModules.Avatar.InstantMessage 41namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
41{ 42{
42 public class PresenceModule : IRegionModule, IPresenceModule 43 public class PresenceModule : ISharedRegionModule, IPresenceModule
43 { 44 {
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 private static readonly ILog m_log = LogManager.GetLogger(
45 46 MethodBase.GetCurrentMethod().DeclaringType);
46 private bool m_Enabled = false;
47 private bool m_Gridmode = false;
48
49 // some default scene for doing things that aren't connected to a specific scene. Avoids locking.
50 private Scene m_initialScene;
51
52 private List<Scene> m_Scenes = new List<Scene>();
53
54 // we currently are only interested in root-agents. If the root isn't here, we don't know the region the
55 // user is in, so we have to ask the messaging server anyway.
56 private Dictionary<UUID, Scene> m_RootAgents =
57 new Dictionary<UUID, Scene>();
58 47
59 public event PresenceChange OnPresenceChange; 48 public event PresenceChange OnPresenceChange;
60 public event BulkPresenceData OnBulkPresenceData; 49 public event BulkPresenceData OnBulkPresenceData;
61 50
62 public void Initialise(Scene scene, IConfigSource config) 51 public void Initialise(IConfigSource config)
63 { 52 {
64 lock (m_Scenes)
65 {
66 // This is a shared module; Initialise will be called for every region on this server.
67 // Only check config once for the first region.
68 if (m_Scenes.Count == 0)
69 {
70 IConfig cnf = config.Configs["Messaging"];
71 if (cnf != null && cnf.GetString(
72 "PresenceModule", "PresenceModule") !=
73 "PresenceModule")
74 return;
75
76 cnf = config.Configs["Startup"];
77 if (cnf != null)
78 m_Gridmode = cnf.GetBoolean("gridmode", false);
79
80 m_Enabled = true;
81
82 m_initialScene = scene;
83 }
84
85 if (m_Gridmode)
86 NotifyMessageServerOfStartup(scene);
87
88 m_Scenes.Add(scene);
89 }
90
91 scene.RegisterModuleInterface<IPresenceModule>(this);
92
93 scene.EventManager.OnNewClient += OnNewClient;
94 scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene;
95 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
96 } 53 }
97 54
98 public void PostInitialise() 55 public void AddRegion(Scene scene)
99 { 56 {
100 } 57 }
101 58
102 public void Close() 59 public void RegionLoaded(Scene scene)
103 { 60 {
104 if (!m_Gridmode || !m_Enabled)
105 return;
106
107 if (OnPresenceChange != null)
108 {
109 lock (m_RootAgents)
110 {
111 // on shutdown, users are kicked, too
112 foreach (KeyValuePair<UUID, Scene> pair in m_RootAgents)
113 {
114 OnPresenceChange(new PresenceInfo(pair.Key, UUID.Zero));
115 }
116 }
117 }
118
119 lock (m_Scenes)
120 {
121 foreach (Scene scene in m_Scenes)
122 NotifyMessageServerOfShutdown(scene);
123 }
124 } 61 }
125 62
126 public string Name 63 public void RemoveRegion(Scene scene)
127 { 64 {
128 get { return "PresenceModule"; }
129 }
130
131 public bool IsSharedModule
132 {
133 get { return true; }
134 }
135
136 public void RequestBulkPresenceData(UUID[] users)
137 {
138 if (OnBulkPresenceData != null)
139 {
140 PresenceInfo[] result = new PresenceInfo[users.Length];
141 if (m_Gridmode)
142 {
143 // first check the local information
144 List<UUID> uuids = new List<UUID>(); // the uuids to check remotely
145 List<int> indices = new List<int>(); // just for performance.
146 lock (m_RootAgents)
147 {
148 for (int i = 0; i < uuids.Count; ++i)
149 {
150 Scene scene;
151 if (m_RootAgents.TryGetValue(users[i], out scene))
152 {
153 result[i] = new PresenceInfo(users[i], scene.RegionInfo.RegionID);
154 }
155 else
156 {
157 uuids.Add(users[i]);
158 indices.Add(i);
159 }
160 }
161 }
162
163 // now we have filtered out all the local root agents. The rest we have to request info about
164 Dictionary<UUID, FriendRegionInfo> infos = m_initialScene.GetFriendRegionInfos(uuids);
165 for (int i = 0; i < uuids.Count; ++i)
166 {
167 FriendRegionInfo info;
168 if (infos.TryGetValue(uuids[i], out info) && info.isOnline)
169 {
170 UUID regionID = info.regionID;
171 if (regionID == UUID.Zero)
172 {
173 // TODO this is the old messaging-server protocol; only the regionHandle is available.
174 // Fetch region-info to get the id
175 uint x = 0, y = 0;
176 Utils.LongToUInts(info.regionHandle, out x, out y);
177 GridRegion regionInfo = m_initialScene.GridService.GetRegionByPosition(m_initialScene.RegionInfo.ScopeID,
178 (int)x, (int)y);
179 regionID = regionInfo.RegionID;
180 }
181 result[indices[i]] = new PresenceInfo(uuids[i], regionID);
182 }
183 else result[indices[i]] = new PresenceInfo(uuids[i], UUID.Zero);
184 }
185 }
186 else
187 {
188 // in standalone mode, we have all the info locally available.
189 lock (m_RootAgents)
190 {
191 for (int i = 0; i < users.Length; ++i)
192 {
193 Scene scene;
194 if (m_RootAgents.TryGetValue(users[i], out scene))
195 {
196 result[i] = new PresenceInfo(users[i], scene.RegionInfo.RegionID);
197 }
198 else
199 {
200 result[i] = new PresenceInfo(users[i], UUID.Zero);
201 }
202 }
203 }
204 }
205
206 // tell everyone
207 OnBulkPresenceData(result);
208 }
209 }
210
211 // new client doesn't mean necessarily that user logged in, it just means it entered one of the
212 // the regions on this server
213 public void OnNewClient(IClientAPI client)
214 {
215 client.OnConnectionClosed += OnConnectionClosed;
216 client.OnLogout += OnLogout;
217
218 // KLUDGE: See handler for details.
219 client.OnEconomyDataRequest += OnEconomyDataRequest;
220 } 65 }
221 66
222 // connection closed just means *one* client connection has been closed. It doesn't mean that the 67 public void PostInitialise()
223 // user has logged off; it might have just TPed away.
224 public void OnConnectionClosed(IClientAPI client)
225 {
226 // TODO: Have to think what we have to do here...
227 // Should we just remove the root from the list (if scene matches)?
228 if (!(client.Scene is Scene))
229 return;
230 Scene scene = (Scene)client.Scene;
231
232 lock (m_RootAgents)
233 {
234 Scene rootScene;
235 if (!(m_RootAgents.TryGetValue(client.AgentId, out rootScene)) || scene != rootScene)
236 return;
237
238 m_RootAgents.Remove(client.AgentId);
239 }
240
241 // Should it have logged off, we'll do the logout part in OnLogout, even if no root is stored
242 // anymore. It logged off, after all...
243 }
244
245 // Triggered when the user logs off.
246 public void OnLogout(IClientAPI client)
247 {
248 if (!(client.Scene is Scene))
249 return;
250 Scene scene = (Scene)client.Scene;
251
252 // On logout, we really remove the client from rootAgents, even if the scene doesn't match
253 lock (m_RootAgents)
254 {
255 if (m_RootAgents.ContainsKey(client.AgentId)) m_RootAgents.Remove(client.AgentId);
256 }
257
258 // now inform the messaging server and anyone who is interested
259 NotifyMessageServerOfAgentLeaving(client.AgentId, scene.RegionInfo.RegionID, scene.RegionInfo.RegionHandle);
260 if (OnPresenceChange != null) OnPresenceChange(new PresenceInfo(client.AgentId, UUID.Zero));
261 }
262
263 public void OnSetRootAgentScene(UUID agentID, Scene scene)
264 {
265 // OnSetRootAgentScene can be called from several threads at once (with different agentID).
266 // Concurrent access to m_RootAgents is prone to failure on multi-core/-processor systems without
267 // correct locking).
268 lock (m_RootAgents)
269 {
270 Scene rootScene;
271 if (m_RootAgents.TryGetValue(agentID, out rootScene) && scene == rootScene)
272 {
273 return;
274 }
275 m_RootAgents[agentID] = scene;
276 }
277
278 // inform messaging server that agent changed the region
279 Util.FireAndForget(
280 delegate(object o)
281 {
282 NotifyMessageServerOfAgentLocation(agentID, scene.RegionInfo.RegionID, scene.RegionInfo.RegionHandle);
283 }
284 );
285 }
286
287 private void OnEconomyDataRequest(UUID agentID)
288 { 68 {
289 // KLUDGE: This is the only way I found to get a message (only) after login was completed and the
290 // client is connected enough to receive UDP packets.
291 // This packet seems to be sent only once, just after connection was established to the first
292 // region after login.
293 // We use it here to trigger a presence update; the old update-on-login was never be heard by
294 // the freshly logged in viewer, as it wasn't connected to the region at that time.
295 // TODO: Feel free to replace this by a better solution if you find one.
296
297 // get the agent. This should work every time, as we just got a packet from it
298 ScenePresence agent = null;
299 lock (m_Scenes)
300 {
301 foreach (Scene scene in m_Scenes)
302 {
303 agent = scene.GetScenePresence(agentID);
304 if (agent != null) break;
305 }
306 }
307
308 // just to be paranoid...
309 if (agent == null)
310 {
311 m_log.ErrorFormat("[PRESENCE]: Got a packet from agent {0} who can't be found anymore!?", agentID);
312 return;
313 }
314
315 // we are a bit premature here, but the next packet will switch this child agent to root.
316 if (OnPresenceChange != null) OnPresenceChange(new PresenceInfo(agentID, agent.Scene.RegionInfo.RegionID));
317 } 69 }
318 70
319 public void OnMakeChildAgent(ScenePresence agent) 71 public void Close()
320 { 72 {
321 // OnMakeChildAgent can be called from several threads at once (with different agent).
322 // Concurrent access to m_RootAgents is prone to failure on multi-core/-processor systems without
323 // correct locking).
324 lock (m_RootAgents)
325 {
326 Scene rootScene;
327 if (m_RootAgents.TryGetValue(agent.UUID, out rootScene) && agent.Scene == rootScene)
328 {
329 m_RootAgents.Remove(agent.UUID);
330 }
331 }
332 // don't notify the messaging-server; either this agent just had been downgraded and another one will be upgraded
333 // to root momentarily (which will notify the messaging-server), or possibly it will be closed in a moment,
334 // which will update the messaging-server, too.
335 } 73 }
336 74
337 private void NotifyMessageServerOfStartup(Scene scene) 75 public string Name
338 { 76 {
339 Hashtable xmlrpcdata = new Hashtable(); 77 get { return "PresenceModule"; }
340 xmlrpcdata["RegionUUID"] = scene.RegionInfo.RegionID.ToString();
341 ArrayList SendParams = new ArrayList();
342 SendParams.Add(xmlrpcdata);
343 try
344 {
345 XmlRpcRequest UpRequest = new XmlRpcRequest("region_startup", SendParams);
346 XmlRpcResponse resp = UpRequest.Send(scene.CommsManager.NetworkServersInfo.MessagingURL, 5000);
347
348 Hashtable responseData = (Hashtable)resp.Value;
349 if (responseData == null || (!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE")
350 {
351 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region startup for region {0}", scene.RegionInfo.RegionName);
352 }
353 }
354 catch (WebException)
355 {
356 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region startup for region {0}", scene.RegionInfo.RegionName);
357 }
358 } 78 }
359 79
360 private void NotifyMessageServerOfShutdown(Scene scene) 80 public Type ReplaceableInterface
361 { 81 {
362 if (m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL == string.Empty) 82 get { return null; }
363 return;
364
365 Hashtable xmlrpcdata = new Hashtable();
366 xmlrpcdata["RegionUUID"] = scene.RegionInfo.RegionID.ToString();
367 ArrayList SendParams = new ArrayList();
368 SendParams.Add(xmlrpcdata);
369 try
370 {
371 XmlRpcRequest DownRequest = new XmlRpcRequest("region_shutdown", SendParams);
372 XmlRpcResponse resp = DownRequest.Send(scene.CommsManager.NetworkServersInfo.MessagingURL, 5000);
373
374 Hashtable responseData = (Hashtable)resp.Value;
375 if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE")
376 {
377 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region shutdown for region {0}", scene.RegionInfo.RegionName);
378 }
379 }
380 catch (WebException)
381 {
382 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region shutdown for region {0}", scene.RegionInfo.RegionName);
383 }
384 } 83 }
385 84
386 private void NotifyMessageServerOfAgentLocation(UUID agentID, UUID region, ulong regionHandle) 85 public void RequestBulkPresenceData(UUID[] users)
387 { 86 {
388 if (m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL == string.Empty)
389 return;
390
391 Hashtable xmlrpcdata = new Hashtable();
392 xmlrpcdata["AgentID"] = agentID.ToString();
393 xmlrpcdata["RegionUUID"] = region.ToString();
394 xmlrpcdata["RegionHandle"] = regionHandle.ToString();
395 ArrayList SendParams = new ArrayList();
396 SendParams.Add(xmlrpcdata);
397 try
398 {
399 XmlRpcRequest LocationRequest = new XmlRpcRequest("agent_location", SendParams);
400 XmlRpcResponse resp = LocationRequest.Send(m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL, 5000);
401
402 Hashtable responseData = (Hashtable)resp.Value;
403 if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE")
404 {
405 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent location for {0}", agentID.ToString());
406 }
407 }
408 catch (WebException)
409 {
410 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent location for {0}", agentID.ToString());
411 }
412 } 87 }
413 88
414 private void NotifyMessageServerOfAgentLeaving(UUID agentID, UUID region, ulong regionHandle) 89 public void OnNewClient(IClientAPI client)
415 { 90 {
416 if (m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL == string.Empty)
417 return;
418
419 Hashtable xmlrpcdata = new Hashtable();
420 xmlrpcdata["AgentID"] = agentID.ToString();
421 xmlrpcdata["RegionUUID"] = region.ToString();
422 xmlrpcdata["RegionHandle"] = regionHandle.ToString();
423 ArrayList SendParams = new ArrayList();
424 SendParams.Add(xmlrpcdata);
425 try
426 {
427 XmlRpcRequest LeavingRequest = new XmlRpcRequest("agent_leaving", SendParams);
428 XmlRpcResponse resp = LeavingRequest.Send(m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL, 5000);
429
430 Hashtable responseData = (Hashtable)resp.Value;
431 if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE")
432 {
433 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent leaving for {0}", agentID.ToString());
434 }
435 }
436 catch (WebException)
437 {
438 m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent leaving for {0}", agentID.ToString());
439 }
440 } 91 }
441 } 92 }
442} 93}
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
index d9a021f..55d4f14 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
@@ -39,7 +39,7 @@ using OpenSim.Services.Interfaces;
39 39
40namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer 40namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
41{ 41{
42 public class InventoryTransferModule : IInventoryTransferModule, IRegionModule 42 public class InventoryTransferModule : IInventoryTransferModule, ISharedRegionModule
43 { 43 {
44 private static readonly ILog m_log 44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -50,10 +50,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
50 new Dictionary<UUID, Scene>(); 50 new Dictionary<UUID, Scene>();
51 51
52 private IMessageTransferModule m_TransferModule = null; 52 private IMessageTransferModule m_TransferModule = null;
53 private bool m_Enabled = true;
53 54
54 #region IRegionModule Members 55 #region IRegionModule Members
55 56
56 public void Initialise(Scene scene, IConfigSource config) 57 public void Initialise(IConfigSource config)
57 { 58 {
58 if (config.Configs["Messaging"] != null) 59 if (config.Configs["Messaging"] != null)
59 { 60 {
@@ -62,29 +63,56 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
62 if (config.Configs["Messaging"].GetString( 63 if (config.Configs["Messaging"].GetString(
63 "InventoryTransferModule", "InventoryTransferModule") != 64 "InventoryTransferModule", "InventoryTransferModule") !=
64 "InventoryTransferModule") 65 "InventoryTransferModule")
66 {
67 m_Enabled = false;
65 return; 68 return;
69 }
66 } 70 }
71 }
67 72
68 if (!m_Scenelist.Contains(scene)) 73 public void AddRegion(Scene scene)
69 { 74 {
70 m_Scenelist.Add(scene); 75 if (!m_Enabled)
76 return;
71 77
72 scene.RegisterModuleInterface<IInventoryTransferModule>(this); 78 m_Scenelist.Add(scene);
73 79
74 scene.EventManager.OnNewClient += OnNewClient; 80 scene.RegisterModuleInterface<IInventoryTransferModule>(this);
75 scene.EventManager.OnClientClosed += ClientLoggedOut; 81
76 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; 82 scene.EventManager.OnNewClient += OnNewClient;
77 } 83 scene.EventManager.OnClientClosed += ClientLoggedOut;
84 scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage;
78 } 85 }
79 86
80 public void PostInitialise() 87 public void RegionLoaded(Scene scene)
81 { 88 {
82 if (m_Scenelist.Count > 0) 89 if (m_TransferModule == null)
83 { 90 {
84 m_TransferModule = m_Scenelist[0].RequestModuleInterface<IMessageTransferModule>(); 91 m_TransferModule = m_Scenelist[0].RequestModuleInterface<IMessageTransferModule>();
85 if (m_TransferModule == null) 92 if (m_TransferModule == null)
93 {
86 m_log.Error("[INVENTORY TRANSFER] No Message transfer module found, transfers will be local only"); 94 m_log.Error("[INVENTORY TRANSFER] No Message transfer module found, transfers will be local only");
95 m_Enabled = false;
96
97 m_Scenelist.Clear();
98 scene.EventManager.OnNewClient -= OnNewClient;
99 scene.EventManager.OnClientClosed -= ClientLoggedOut;
100 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
101 }
87 } 102 }
103
104 }
105
106 public void RemoveRegion(Scene scene)
107 {
108 scene.EventManager.OnNewClient -= OnNewClient;
109 scene.EventManager.OnClientClosed -= ClientLoggedOut;
110 scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage;
111 m_Scenelist.Remove(scene);
112 }
113
114 public void PostInitialise()
115 {
88 } 116 }
89 117
90 public void Close() 118 public void Close()
@@ -96,9 +124,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
96 get { return "InventoryModule"; } 124 get { return "InventoryModule"; }
97 } 125 }
98 126
99 public bool IsSharedModule 127 public Type ReplaceableInterface
100 { 128 {
101 get { return true; } 129 get { return null; }
102 } 130 }
103 131
104 #endregion 132 #endregion
diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
index 261bd6c..d1d7df2 100644
--- a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs
@@ -37,34 +37,72 @@ using OpenSim.Region.Framework.Scenes;
37 37
38namespace OpenSim.Region.CoreModules.Avatar.Lure 38namespace OpenSim.Region.CoreModules.Avatar.Lure
39{ 39{
40 public class LureModule : IRegionModule 40 public class LureModule : ISharedRegionModule
41 { 41 {
42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 42 private static readonly ILog m_log = LogManager.GetLogger(
43 MethodBase.GetCurrentMethod().DeclaringType);
43 44
44 private readonly List<Scene> m_scenes = new List<Scene>(); 45 private readonly List<Scene> m_scenes = new List<Scene>();
45 46
46 private IMessageTransferModule m_TransferModule = null; 47 private IMessageTransferModule m_TransferModule = null;
48 private bool m_Enabled = true;
47 49
48 public void Initialise(Scene scene, IConfigSource config) 50 public void Initialise(IConfigSource config)
49 { 51 {
50 if (config.Configs["Messaging"] != null) 52 if (config.Configs["Messaging"] != null)
51 { 53 {
52 if (config.Configs["Messaging"].GetString( 54 if (config.Configs["Messaging"].GetString(
53 "LureModule", "LureModule") != 55 "LureModule", "LureModule") !=
54 "LureModule") 56 "LureModule")
55 return; 57 m_Enabled = false;
56 } 58 }
59 }
60
61 public void AddRegion(Scene scene)
62 {
63 if (!m_Enabled)
64 return;
57 65
58 lock (m_scenes) 66 lock (m_scenes)
59 { 67 {
60 if (!m_scenes.Contains(scene)) 68 m_scenes.Add(scene);
69 scene.EventManager.OnNewClient += OnNewClient;
70 scene.EventManager.OnIncomingInstantMessage +=
71 OnGridInstantMessage;
72 }
73 }
74
75 public void RegionLoaded(Scene scene)
76 {
77 if (m_TransferModule == null)
78 {
79 m_TransferModule =
80 scene.RequestModuleInterface<IMessageTransferModule>();
81
82 if (m_TransferModule == null)
61 { 83 {
62 m_scenes.Add(scene); 84 m_log.Error("[INSTANT MESSAGE]: No message transfer module, "+
63 scene.EventManager.OnNewClient += OnNewClient; 85 "lures will not work!");
64 scene.EventManager.OnIncomingInstantMessage += 86
87 m_Enabled = false;
88 m_scenes.Clear();
89 scene.EventManager.OnNewClient -= OnNewClient;
90 scene.EventManager.OnIncomingInstantMessage -=
65 OnGridInstantMessage; 91 OnGridInstantMessage;
66 } 92 }
67 } 93 }
94
95 }
96
97 public void RemoveRegion(Scene scene)
98 {
99 lock (m_scenes)
100 {
101 m_scenes.Remove(scene);
102 scene.EventManager.OnNewClient -= OnNewClient;
103 scene.EventManager.OnIncomingInstantMessage -=
104 OnGridInstantMessage;
105 }
68 } 106 }
69 107
70 void OnNewClient(IClientAPI client) 108 void OnNewClient(IClientAPI client)
@@ -76,12 +114,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure
76 114
77 public void PostInitialise() 115 public void PostInitialise()
78 { 116 {
79 m_TransferModule =
80 m_scenes[0].RequestModuleInterface<IMessageTransferModule>();
81
82 if (m_TransferModule == null)
83 m_log.Error("[INSTANT MESSAGE]: No message transfer module, "+
84 "lures will not work!");
85 } 117 }
86 118
87 public void Close() 119 public void Close()
@@ -93,9 +125,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure
93 get { return "LureModule"; } 125 get { return "LureModule"; }
94 } 126 }
95 127
96 public bool IsSharedModule 128 public Type ReplaceableInterface
97 { 129 {
98 get { return true; } 130 get { return null; }
99 } 131 }
100 132
101 public void OnInstantMessage(IClientAPI client, GridInstantMessage im) 133 public void OnInstantMessage(IClientAPI client, GridInstantMessage im)
diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
index beb2307..40a13f4 100644
--- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
+++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml
@@ -17,6 +17,14 @@
17 <RegionModule id="HGWorldMapModule" type="OpenSim.Region.CoreModules.Hypergrid.HGWorldMapModule" /> 17 <RegionModule id="HGWorldMapModule" type="OpenSim.Region.CoreModules.Hypergrid.HGWorldMapModule" />
18 <RegionModule id="UrlModule" type="OpenSim.Region.CoreModules.Scripting.LSLHttp.UrlModule" /> 18 <RegionModule id="UrlModule" type="OpenSim.Region.CoreModules.Scripting.LSLHttp.UrlModule" />
19 <RegionModule id="Chat" type="OpenSim.Region.CoreModules.Avatar.Chat.ChatModule" /> 19 <RegionModule id="Chat" type="OpenSim.Region.CoreModules.Avatar.Chat.ChatModule" />
20 <RegionModule id="FriendsModule" type="OpenSim.Region.CoreModules.Avatar.Friends.FriendsModule" />
21 <RegionModule id="PresenceModule" type="OpenSim.Region.CoreModules.Avatar.InstantMessage.PresenceModule" />
22 <RegionModule id="MuteListModule" type="OpenSim.Region.CoreModules.Avatar.InstantMessage.MuteListModule" />
23 <RegionModule id="OfflineMessageModule" type="OpenSim.Region.CoreModules.Avatar.InstantMessage.OfflineMessageModule" />
24 <RegionModule id="InstantMessageModule" type="OpenSim.Region.CoreModules.Avatar.InstantMessage.InstantMessageModule" />
25 <RegionModule id="MessageTransferModule" type="OpenSim.Region.CoreModules.Avatar.InstantMessage.MessageTransferModule" />
26 <RegionModule id="LureModule" type="OpenSim.Region.CoreModules.Avatar.Lure.LureModule" />
27 <RegionModule id="InventoryTransferModule" type="OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.InventoryTransferModule" />
20 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.CoreAssetCache" /> 28 <RegionModule id="CoreAssetCache" type="OpenSim.Region.CoreModules.Asset.CoreAssetCache" />
21 <RegionModule id="GlynnTuckerAssetCache" type="OpenSim.Region.CoreModules.Asset.GlynnTuckerAssetCache" /> 29 <RegionModule id="GlynnTuckerAssetCache" type="OpenSim.Region.CoreModules.Asset.GlynnTuckerAssetCache" />
22 <RegionModule id="CenomeMemoryAssetCache" type="OpenSim.Region.CoreModules.Asset.CenomeMemoryAssetCache"/> 30 <RegionModule id="CenomeMemoryAssetCache" type="OpenSim.Region.CoreModules.Asset.CenomeMemoryAssetCache"/>
diff --git a/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs b/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs
index 630c6a3..d44c1e1 100644
--- a/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IPresenceModule.cs
@@ -31,13 +31,13 @@ namespace OpenSim.Region.Framework.Interfaces
31{ 31{
32 public struct PresenceInfo 32 public struct PresenceInfo
33 { 33 {
34 public UUID userID; 34 public string UserID;
35 public UUID regionID; 35 public UUID RegionID;
36 36
37 public PresenceInfo(UUID userID, UUID regionID) 37 public PresenceInfo(string userID, UUID regionID)
38 { 38 {
39 this.userID = userID; 39 UserID = userID;
40 this.regionID = regionID; 40 RegionID = regionID;
41 } 41 }
42 } 42 }
43 43