aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
diff options
context:
space:
mode:
authorHomer Horwitz2008-11-01 22:09:48 +0000
committerHomer Horwitz2008-11-01 22:09:48 +0000
commit38e8853e5761d09a7e8f580dd277d9b99b834696 (patch)
tree653fe4c9075a03c05a4b5782f7309afa83062e5c /OpenSim
parent* minor: Remove mono compiler warning (diff)
downloadopensim-SC_OLD-38e8853e5761d09a7e8f580dd277d9b99b834696.zip
opensim-SC_OLD-38e8853e5761d09a7e8f580dd277d9b99b834696.tar.gz
opensim-SC_OLD-38e8853e5761d09a7e8f580dd277d9b99b834696.tar.bz2
opensim-SC_OLD-38e8853e5761d09a7e8f580dd277d9b99b834696.tar.xz
Megapatch that fixes/adds: friend offer/deny/accept, friendship termination,
on-/offline updates, calling cards for friends. This adds methods in the DB layer and changes the MessagingServer, so a full update (incl. UGAIM) is necessary to get it working. Older regions shouldn't break, nor should older UGAIM break newer regions, but friends/presence will only work with all concerned parts (UGAIM, source region and destination region) at this revision (or later). I added the DB code for MSSQL, too, but couldn't test that. BEWARE: May contain bugs.
Diffstat (limited to 'OpenSim')
-rw-r--r--OpenSim/Data/MSSQL/MSSQLUserData.cs33
-rw-r--r--OpenSim/Data/MySQL/MySQLUserData.cs43
-rw-r--r--OpenSim/Data/NHibernate/NHibernateUserData.cs1
-rw-r--r--OpenSim/Data/SQLite/SQLiteUserData.cs22
-rw-r--r--OpenSim/Data/UserDataBase.cs1
-rw-r--r--OpenSim/Framework/Communications/CommunicationsManager.cs21
-rw-r--r--OpenSim/Framework/Communications/IInterRegionCommunications.cs38
-rw-r--r--OpenSim/Framework/Communications/IMessagingService.cs37
-rw-r--r--OpenSim/Framework/Communications/UserManagerBase.cs24
-rw-r--r--OpenSim/Framework/FriendListItem.cs2
-rw-r--r--OpenSim/Framework/FriendRegionInfo.cs37
-rw-r--r--OpenSim/Framework/IClientAPI.cs2
-rw-r--r--OpenSim/Framework/IUserData.cs11
-rw-r--r--OpenSim/Framework/NetworkServersInfo.cs4
-rw-r--r--OpenSim/Framework/Servers/BaseHttpServer.cs13
-rw-r--r--OpenSim/Grid/MessagingServer/Main.cs1
-rw-r--r--OpenSim/Grid/MessagingServer/MessageService.cs465
-rw-r--r--OpenSim/Grid/MessagingServer/UserPresenceData.cs2
-rw-r--r--OpenSim/Region/Application/OpenSimBase.cs3
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs23
-rw-r--r--OpenSim/Region/Communications/Local/CommunicationsLocal.cs4
-rw-r--r--OpenSim/Region/Communications/Local/LocalBackEndServices.cs24
-rw-r--r--OpenSim/Region/Communications/OGS1/CommunicationsOGS1.cs4
-rw-r--r--OpenSim/Region/Communications/OGS1/OGS1GridServices.cs80
-rw-r--r--OpenSim/Region/Communications/OGS1/OGS1UserServices.cs60
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs1137
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs10
-rw-r--r--OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs4
-rw-r--r--OpenSim/Region/Environment/Scenes/Scene.cs14
-rw-r--r--OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs4
30 files changed, 1384 insertions, 740 deletions
diff --git a/OpenSim/Data/MSSQL/MSSQLUserData.cs b/OpenSim/Data/MSSQL/MSSQLUserData.cs
index ee7765f..4d4b3bc 100644
--- a/OpenSim/Data/MSSQL/MSSQLUserData.cs
+++ b/OpenSim/Data/MSSQL/MSSQLUserData.cs
@@ -570,6 +570,39 @@ namespace OpenSim.Data.MSSQL
570 return friendList; 570 return friendList;
571 } 571 }
572 572
573 override public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids)
574 {
575 Dictionary<UUID, FriendRegionInfo> infos = new Dictionary<UUID,FriendRegionInfo>();
576 try
577 {
578 foreach (UUID uuid in uuids)
579 {
580 using (AutoClosingSqlCommand command = database.Query(
581 "select agentOnline,currentHandle from " + m_agentsTableName + " where UUID = @uuid"))
582 {
583 command.Parameters.Add(database.CreateParameter("@uuid", uuid));
584
585 using (IDataReader reader = command.ExecuteReader())
586 {
587 while (reader.Read())
588 {
589 FriendRegionInfo fri = new FriendRegionInfo();
590 fri.isOnline = (sbyte)reader["agentOnline"] != 0;
591 fri.regionHandle = (ulong)reader["currentHandle"];
592
593 infos[uuid] = fri;
594 }
595 }
596 }
597 }
598 }
599 catch (Exception e)
600 {
601 m_log.Warn("[MSSQL]: Got exception on trying to find friends regions:", e);
602 }
603
604 return infos;
605 }
573 #endregion 606 #endregion
574 607
575 #region Money functions (not used) 608 #region Money functions (not used)
diff --git a/OpenSim/Data/MySQL/MySQLUserData.cs b/OpenSim/Data/MySQL/MySQLUserData.cs
index e671989..c3aade0 100644
--- a/OpenSim/Data/MySQL/MySQLUserData.cs
+++ b/OpenSim/Data/MySQL/MySQLUserData.cs
@@ -365,6 +365,49 @@ namespace OpenSim.Data.MySQL
365 return Lfli; 365 return Lfli;
366 } 366 }
367 367
368 override public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids)
369 {
370 MySQLSuperManager dbm = GetLockedConnection("GetFriendRegionInfos");
371 Dictionary<UUID, FriendRegionInfo> infos = new Dictionary<UUID,FriendRegionInfo>();
372
373 try
374 {
375 foreach (UUID uuid in uuids)
376 {
377 Dictionary<string, string> param = new Dictionary<string, string>();
378 param["?uuid"] = uuid.ToString();
379 IDbCommand result =
380 dbm.Manager.Query("select agentOnline,currentHandle from " + m_agentsTableName +
381 " where UUID = ?uuid", param);
382
383 IDataReader reader = result.ExecuteReader();
384 while (reader.Read())
385 {
386 FriendRegionInfo fri = new FriendRegionInfo();
387 fri.isOnline = (sbyte)reader["agentOnline"] != 0;
388 fri.regionHandle = (ulong)reader["currentHandle"];
389
390 infos[uuid] = fri;
391 }
392
393 reader.Dispose();
394 result.Dispose();
395 }
396 }
397 catch (Exception e)
398 {
399 m_log.Warn("[MYSQL]: Got exception on trying to find friends regions:", e);
400 dbm.Manager.Reconnect();
401 m_log.Error(e.ToString());
402 }
403 finally
404 {
405 dbm.Release();
406 }
407
408 return infos;
409 }
410
368 #endregion 411 #endregion
369 412
370 public override void UpdateUserCurrentRegion(UUID avatarid, UUID regionuuid, ulong regionhandle) 413 public override void UpdateUserCurrentRegion(UUID avatarid, UUID regionuuid, ulong regionhandle)
diff --git a/OpenSim/Data/NHibernate/NHibernateUserData.cs b/OpenSim/Data/NHibernate/NHibernateUserData.cs
index 459b8e7..5ef48c8 100644
--- a/OpenSim/Data/NHibernate/NHibernateUserData.cs
+++ b/OpenSim/Data/NHibernate/NHibernateUserData.cs
@@ -255,6 +255,7 @@ namespace OpenSim.Data.NHibernate
255 public override void RemoveUserFriend(UUID friendlistowner, UUID friend) { return; } 255 public override void RemoveUserFriend(UUID friendlistowner, UUID friend) { return; }
256 public override void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms) { return; } 256 public override void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms) { return; }
257 public override List<FriendListItem> GetUserFriendList(UUID friendlistowner) { return new List<FriendListItem>(); } 257 public override List<FriendListItem> GetUserFriendList(UUID friendlistowner) { return new List<FriendListItem>(); }
258 public override Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids) { return new Dictionary<UUID, FriendRegionInfo>(); }
258 public override bool MoneyTransferRequest(UUID from, UUID to, uint amount) { return true; } 259 public override bool MoneyTransferRequest(UUID from, UUID to, uint amount) { return true; }
259 public override bool InventoryTransferRequest(UUID from, UUID to, UUID inventory) { return true; } 260 public override bool InventoryTransferRequest(UUID from, UUID to, UUID inventory) { return true; }
260 261
diff --git a/OpenSim/Data/SQLite/SQLiteUserData.cs b/OpenSim/Data/SQLite/SQLiteUserData.cs
index 82db481..2f0863e 100644
--- a/OpenSim/Data/SQLite/SQLiteUserData.cs
+++ b/OpenSim/Data/SQLite/SQLiteUserData.cs
@@ -332,8 +332,28 @@ namespace OpenSim.Data.SQLite
332 return returnlist; 332 return returnlist;
333 } 333 }
334 334
335 override public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids)
336 {
337 Dictionary<UUID, FriendRegionInfo> infos = new Dictionary<UUID,FriendRegionInfo>();
335 338
336 339 DataTable agents = ds.Tables["useragents"];
340 foreach (UUID uuid in uuids)
341 {
342 lock (ds)
343 {
344 DataRow row = agents.Rows.Find(Util.ToRawUuidString(uuid));
345 if (row == null) infos[uuid] = null;
346 else
347 {
348 FriendRegionInfo fri = new FriendRegionInfo();
349 fri.isOnline = (bool)row["agentOnline"];
350 fri.regionHandle = Convert.ToUInt64(row["currentHandle"]);
351 infos[uuid] = fri;
352 }
353 }
354 }
355 return infos;
356 }
337 357
338 #endregion 358 #endregion
339 359
diff --git a/OpenSim/Data/UserDataBase.cs b/OpenSim/Data/UserDataBase.cs
index 33b8a94..a5fc8c8 100644
--- a/OpenSim/Data/UserDataBase.cs
+++ b/OpenSim/Data/UserDataBase.cs
@@ -53,6 +53,7 @@ namespace OpenSim.Data
53 public abstract void RemoveUserFriend(UUID friendlistowner, UUID friend); 53 public abstract void RemoveUserFriend(UUID friendlistowner, UUID friend);
54 public abstract void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms); 54 public abstract void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms);
55 public abstract List<FriendListItem> GetUserFriendList(UUID friendlistowner); 55 public abstract List<FriendListItem> GetUserFriendList(UUID friendlistowner);
56 public abstract Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids);
56 public abstract bool MoneyTransferRequest(UUID from, UUID to, uint amount); 57 public abstract bool MoneyTransferRequest(UUID from, UUID to, uint amount);
57 public abstract bool InventoryTransferRequest(UUID from, UUID to, UUID inventory); 58 public abstract bool InventoryTransferRequest(UUID from, UUID to, UUID inventory);
58 public abstract List<AvatarPickerAvatar> GeneratePickerResults(UUID queryID, string query); 59 public abstract List<AvatarPickerAvatar> GeneratePickerResults(UUID queryID, string query);
diff --git a/OpenSim/Framework/Communications/CommunicationsManager.cs b/OpenSim/Framework/Communications/CommunicationsManager.cs
index dfe0fdc..bb4a853 100644
--- a/OpenSim/Framework/Communications/CommunicationsManager.cs
+++ b/OpenSim/Framework/Communications/CommunicationsManager.cs
@@ -50,6 +50,12 @@ namespace OpenSim.Framework.Communications
50 } 50 }
51 protected IUserService m_userService; 51 protected IUserService m_userService;
52 52
53 public IMessagingService MessageService
54 {
55 get { return m_messageService; }
56 }
57 protected IMessagingService m_messageService;
58
53 public IGridServices GridService 59 public IGridServices GridService
54 { 60 {
55 get { return m_gridService; } 61 get { return m_gridService; }
@@ -370,6 +376,21 @@ namespace OpenSim.Framework.Communications
370 return m_userService.GetUserFriendList(friendlistowner); 376 return m_userService.GetUserFriendList(friendlistowner);
371 } 377 }
372 378
379 public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos(List<UUID> uuids)
380 {
381 return m_messageService.GetFriendRegionInfos(uuids);
382 }
383
384 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
385 {
386 return m_interRegion.InformFriendsInOtherRegion(agentId, destRegionHandle, friends, online);
387 }
388
389 public bool TriggerTerminateFriend(ulong regionHandle, UUID agentID, UUID exFriendID)
390 {
391 return m_interRegion.TriggerTerminateFriend(regionHandle, agentID, exFriendID);
392 }
393
373 #endregion 394 #endregion
374 395
375 #region Packet Handlers 396 #region Packet Handlers
diff --git a/OpenSim/Framework/Communications/IInterRegionCommunications.cs b/OpenSim/Framework/Communications/IInterRegionCommunications.cs
index 3dd5561..6b589b9 100644
--- a/OpenSim/Framework/Communications/IInterRegionCommunications.cs
+++ b/OpenSim/Framework/Communications/IInterRegionCommunications.cs
@@ -25,6 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System.Collections.Generic;
28using OpenMetaverse; 29using OpenMetaverse;
29 30
30namespace OpenSim.Framework.Communications 31namespace OpenSim.Framework.Communications
@@ -46,5 +47,42 @@ namespace OpenSim.Framework.Communications
46 bool AcknowledgePrimCrossed(ulong regionHandle, UUID primID); 47 bool AcknowledgePrimCrossed(ulong regionHandle, UUID primID);
47 48
48 bool TellRegionToCloseChildConnection(ulong regionHandle, UUID agentID); 49 bool TellRegionToCloseChildConnection(ulong regionHandle, UUID agentID);
50
51 /// <summary>
52 /// Try to inform friends in the given region about online status of agent.
53 /// </summary>
54 /// <param name="agentId">
55 /// The <see cref="UUID"/> of the agent.
56 /// </param>
57 /// <param name="destRegionHandle">
58 /// The regionHandle of the region.
59 /// </param>
60 /// <param name="friends">
61 /// A List of <see cref="UUID"/>s of friends to inform in the given region.
62 /// </param>
63 /// <param name="online">
64 /// Is the agent online or offline
65 /// </param>
66 /// <returns>
67 /// A list of friends that couldn't be reached on this region.
68 /// </returns>
69 List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online);
70
71 /// <summary>
72 /// Send TerminateFriend of exFriendID to agent agentID in region regionHandle.
73 /// </summary>
74 /// <param name="regionHandle">
75 /// The handle of the region agentID is in (hopefully).
76 /// </param>
77 /// <param name="agentID">
78 /// The agent to send the packet to.
79 /// </param>
80 /// <param name="exFriendID">
81 /// The ex-friends ID.
82 /// </param>
83 /// <returns>
84 /// Whether the packet could be sent. False if the agent couldn't be found in the region.
85 /// </returns>
86 bool TriggerTerminateFriend(ulong regionHandle, UUID agentID, UUID exFriendID);
49 } 87 }
50} 88}
diff --git a/OpenSim/Framework/Communications/IMessagingService.cs b/OpenSim/Framework/Communications/IMessagingService.cs
new file mode 100644
index 0000000..5d4cbf8
--- /dev/null
+++ b/OpenSim/Framework/Communications/IMessagingService.cs
@@ -0,0 +1,37 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Collections.Generic;
29using OpenMetaverse;
30
31namespace OpenSim.Framework.Communications
32{
33 public interface IMessagingService
34 {
35 Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids);
36 }
37}
diff --git a/OpenSim/Framework/Communications/UserManagerBase.cs b/OpenSim/Framework/Communications/UserManagerBase.cs
index ba9cf27..7189eee 100644
--- a/OpenSim/Framework/Communications/UserManagerBase.cs
+++ b/OpenSim/Framework/Communications/UserManagerBase.cs
@@ -35,6 +35,7 @@ using OpenMetaverse;
35using OpenMetaverse.StructuredData; 35using OpenMetaverse.StructuredData;
36using log4net; 36using log4net;
37using Nwc.XmlRpc; 37using Nwc.XmlRpc;
38using OpenSim.Framework;
38using OpenSim.Framework.Statistics; 39using OpenSim.Framework.Statistics;
39 40
40namespace OpenSim.Framework.Communications 41namespace OpenSim.Framework.Communications
@@ -42,7 +43,7 @@ namespace OpenSim.Framework.Communications
42 /// <summary> 43 /// <summary>
43 /// Base class for user management (create, read, etc) 44 /// Base class for user management (create, read, etc)
44 /// </summary> 45 /// </summary>
45 public abstract class UserManagerBase : IUserService, IUserServiceAdmin, IAvatarService 46 public abstract class UserManagerBase : IUserService, IUserServiceAdmin, IAvatarService, IMessagingService
46 { 47 {
47 private static readonly ILog m_log 48 private static readonly ILog m_log
48 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 49 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -285,6 +286,27 @@ namespace OpenSim.Framework.Communications
285 return null; 286 return null;
286 } 287 }
287 288
289 public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids)
290 {
291 foreach (IUserDataPlugin plugin in _plugins)
292 {
293 try
294 {
295 Dictionary<UUID, FriendRegionInfo> result = plugin.GetFriendRegionInfos(uuids);
296
297 if (result != null)
298 {
299 return result;
300 }
301 }
302 catch (Exception e)
303 {
304 m_log.Info("[USERSTORAGE]: Unable to GetFriendRegionInfos via " + plugin.Name + "(" + e.ToString() + ")");
305 }
306 }
307 return null;
308 }
309
288 public void StoreWebLoginKey(UUID agentID, UUID webLoginKey) 310 public void StoreWebLoginKey(UUID agentID, UUID webLoginKey)
289 { 311 {
290 foreach (IUserDataPlugin plugin in _plugins) 312 foreach (IUserDataPlugin plugin in _plugins)
diff --git a/OpenSim/Framework/FriendListItem.cs b/OpenSim/Framework/FriendListItem.cs
index 2861ce2..a60bc22 100644
--- a/OpenSim/Framework/FriendListItem.cs
+++ b/OpenSim/Framework/FriendListItem.cs
@@ -35,9 +35,9 @@ namespace OpenSim.Framework
35 public UUID FriendListOwner; 35 public UUID FriendListOwner;
36 36
37 // These are what the list owner gives the friend permission to do 37 // These are what the list owner gives the friend permission to do
38 public uint FriendListOwnerPerms;
38 39
39 // These are what the friend gives the listowner permission to do 40 // These are what the friend gives the listowner permission to do
40 public uint FriendListOwnerPerms;
41 public uint FriendPerms; 41 public uint FriendPerms;
42 42
43 public bool onlinestatus = false; 43 public bool onlinestatus = false;
diff --git a/OpenSim/Framework/FriendRegionInfo.cs b/OpenSim/Framework/FriendRegionInfo.cs
new file mode 100644
index 0000000..04e00e8
--- /dev/null
+++ b/OpenSim/Framework/FriendRegionInfo.cs
@@ -0,0 +1,37 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29
30namespace OpenSim.Framework
31{
32 public class FriendRegionInfo
33 {
34 public bool isOnline;
35 public ulong regionHandle;
36 }
37}
diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs
index 538a2e7..94bf3aa 100644
--- a/OpenSim/Framework/IClientAPI.cs
+++ b/OpenSim/Framework/IClientAPI.cs
@@ -925,6 +925,8 @@ namespace OpenSim.Framework
925 void SendAcceptCallingCard(UUID transactionID); 925 void SendAcceptCallingCard(UUID transactionID);
926 void SendDeclineCallingCard(UUID transactionID); 926 void SendDeclineCallingCard(UUID transactionID);
927 927
928 void SendTerminateFriend(UUID exFriendID);
929
928 void KillEndDone(); 930 void KillEndDone();
929 } 931 }
930} 932}
diff --git a/OpenSim/Framework/IUserData.cs b/OpenSim/Framework/IUserData.cs
index 4089253..07159cc 100644
--- a/OpenSim/Framework/IUserData.cs
+++ b/OpenSim/Framework/IUserData.cs
@@ -136,6 +136,17 @@ namespace OpenSim.Framework
136 List<FriendListItem> GetUserFriendList(UUID friendlistowner); 136 List<FriendListItem> GetUserFriendList(UUID friendlistowner);
137 137
138 /// <summary> 138 /// <summary>
139 /// Returns a list of <see cref="FriendRegionInfo/>s for the specified UUIDs.
140 /// </summary>
141 /// <param name="uuids">
142 /// A <see cref="List"/> of <see cref="UUID/>s to fetch info for
143 /// </param>
144 /// <returns>
145 /// A <see cref="Dictionary"/>, mapping the <see cref="UUID"/>s to <see cref="FriendRegionInfo"/>s.
146 /// </returns>
147 Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos(List<UUID> uuids);
148
149 /// <summary>
139 /// Attempts to move currency units between accounts (NOT RELIABLE / TRUSTWORTHY. DONT TRY RUN YOUR OWN CURRENCY EXCHANGE WITH REAL VALUES) 150 /// Attempts to move currency units between accounts (NOT RELIABLE / TRUSTWORTHY. DONT TRY RUN YOUR OWN CURRENCY EXCHANGE WITH REAL VALUES)
140 /// </summary> 151 /// </summary>
141 /// <param name="from">The account to transfer from</param> 152 /// <param name="from">The account to transfer from</param>
diff --git a/OpenSim/Framework/NetworkServersInfo.cs b/OpenSim/Framework/NetworkServersInfo.cs
index 9f3014d..49b8ef9 100644
--- a/OpenSim/Framework/NetworkServersInfo.cs
+++ b/OpenSim/Framework/NetworkServersInfo.cs
@@ -53,6 +53,7 @@ namespace OpenSim.Framework
53 public string HttpSSLCN = ""; 53 public string HttpSSLCN = "";
54 public uint httpSSLPort = 9001; 54 public uint httpSSLPort = 9001;
55 55
56 public string MessagingURL = String.Empty;
56 57
57 public NetworkServersInfo() 58 public NetworkServersInfo()
58 { 59 {
@@ -102,6 +103,9 @@ namespace OpenSim.Framework
102 "http://127.0.0.1:" + 103 "http://127.0.0.1:" +
103 InventoryConfig.DefaultHttpPort.ToString()); 104 InventoryConfig.DefaultHttpPort.ToString());
104 secureInventoryServer = config.Configs["Network"].GetBoolean("secure_inventory_server", true); 105 secureInventoryServer = config.Configs["Network"].GetBoolean("secure_inventory_server", true);
106
107 MessagingURL = config.Configs["Network"].GetString("messaging_server_url",
108 "http://127.0.0.1:" + MessageServerConfig.DefaultHttpPort);
105 } 109 }
106 } 110 }
107} 111}
diff --git a/OpenSim/Framework/Servers/BaseHttpServer.cs b/OpenSim/Framework/Servers/BaseHttpServer.cs
index 871ea57..98b44db 100644
--- a/OpenSim/Framework/Servers/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/BaseHttpServer.cs
@@ -586,7 +586,18 @@ namespace OpenSim.Framework.Servers
586 XmlRpcMethod method; 586 XmlRpcMethod method;
587 if (m_rpcHandlers.TryGetValue(methodName, out method)) 587 if (m_rpcHandlers.TryGetValue(methodName, out method))
588 { 588 {
589 xmlRpcResponse = method(xmlRprcRequest); 589 try
590 {
591 xmlRpcResponse = method(xmlRprcRequest);
592 }
593 catch(Exception e)
594 {
595 // if the registered XmlRpc method threw an exception, we pass a fault-code along
596 xmlRpcResponse = new XmlRpcResponse();
597 // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
598 xmlRpcResponse.SetFault(-32603, String.Format("Requested method [{0}] threw exception: {1}",
599 methodName, e.Message));
600 }
590 // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here 601 // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here
591 response.KeepAlive = m_rpcHandlersKeepAlive[methodName]; 602 response.KeepAlive = m_rpcHandlersKeepAlive[methodName];
592 } 603 }
diff --git a/OpenSim/Grid/MessagingServer/Main.cs b/OpenSim/Grid/MessagingServer/Main.cs
index 998f3fa..8472571 100644
--- a/OpenSim/Grid/MessagingServer/Main.cs
+++ b/OpenSim/Grid/MessagingServer/Main.cs
@@ -87,6 +87,7 @@ namespace OpenSim.Grid.MessagingServer
87 87
88 m_httpServer.AddXmlRPCHandler("login_to_simulator", msgsvc.UserLoggedOn); 88 m_httpServer.AddXmlRPCHandler("login_to_simulator", msgsvc.UserLoggedOn);
89 m_httpServer.AddXmlRPCHandler("logout_of_simulator", msgsvc.UserLoggedOff); 89 m_httpServer.AddXmlRPCHandler("logout_of_simulator", msgsvc.UserLoggedOff);
90 m_httpServer.AddXmlRPCHandler("get_presence_info_bulk", msgsvc.GetPresenceInfoBulk);
90 91
91 m_httpServer.Start(); 92 m_httpServer.Start();
92 m_log.Info("[SERVER]: Userserver registration was successful"); 93 m_log.Info("[SERVER]: Userserver registration was successful");
diff --git a/OpenSim/Grid/MessagingServer/MessageService.cs b/OpenSim/Grid/MessagingServer/MessageService.cs
index 27892df..0be58e3 100644
--- a/OpenSim/Grid/MessagingServer/MessageService.cs
+++ b/OpenSim/Grid/MessagingServer/MessageService.cs
@@ -37,8 +37,6 @@ using Nwc.XmlRpc;
37using OpenSim.Data; 37using OpenSim.Data;
38using OpenSim.Framework; 38using OpenSim.Framework;
39 39
40//using System.Xml;
41
42namespace OpenSim.Grid.MessagingServer 40namespace OpenSim.Grid.MessagingServer
43{ 41{
44 public class MessageService 42 public class MessageService
@@ -48,17 +46,11 @@ namespace OpenSim.Grid.MessagingServer
48 private MessageServerConfig m_cfg; 46 private MessageServerConfig m_cfg;
49 private UserManager m_userManager; 47 private UserManager m_userManager;
50 48
51 //A hashtable of all current presences this server knows about 49 // a dictionary of all current presences this server knows about
52 private Hashtable m_presences = new Hashtable(); 50 private Dictionary<UUID, UserPresenceData> m_presences = new Dictionary<UUID,UserPresenceData>();
53
54 //a hashtable of all current regions this server knows about
55 private Hashtable m_regionInfoCache = new Hashtable();
56
57 //A hashtable containing lists of UUIDs keyed by UUID for fast backreferencing
58 private Hashtable m_presence_BackReferences = new Hashtable();
59 51
60 // Hashtable containing work units that need to be processed 52 // a dictionary of all current regions this server knows about
61 // private Hashtable m_unProcessedWorkUnits = new Hashtable(); 53 private Dictionary<ulong, RegionProfileData> m_regionInfoCache = new Dictionary<ulong,RegionProfileData>();
62 54
63 public MessageService(MessageServerConfig cfg) 55 public MessageService(MessageServerConfig cfg)
64 { 56 {
@@ -78,174 +70,99 @@ namespace OpenSim.Grid.MessagingServer
78 /// Process Friendlist subscriptions for a user 70 /// Process Friendlist subscriptions for a user
79 /// The login method calls this for a User 71 /// The login method calls this for a User
80 /// </summary> 72 /// </summary>
81 /// <param name="userpresence">The Agent we're processing the friendlist subscriptions</param> 73 /// <param name="userpresence">The Agent we're processing the friendlist subscriptions for</param>
82 public void ProcessFriendListSubscriptions(UserPresenceData userpresence) 74 private void ProcessFriendListSubscriptions(UserPresenceData userpresence)
83 { 75 {
84 lock (m_presences) 76 lock (m_presences)
85 { 77 {
86 if (!m_presences.Contains(userpresence.agentData.AgentID)) 78 m_presences[userpresence.agentData.AgentID] = userpresence;
87 m_presences.Add(userpresence.agentData.AgentID, userpresence);
88 else
89 m_presences[userpresence.agentData.AgentID] = userpresence;
90 } 79 }
91 80
92 List<FriendListItem> uFriendList = userpresence.friendData; 81 Dictionary<UUID, FriendListItem> uFriendList = userpresence.friendData;
93 for (int i = 0; i < uFriendList.Count; i++) 82 foreach (KeyValuePair<UUID, FriendListItem> pair in uFriendList)
94 { 83 {
95 //m_presence_BackReferences.Add(userpresence.agentData.AgentID, uFriendList[i].Friend); 84 UserPresenceData friendup = null;
96 // m_presence_BackReferences.Add(uFriendList[i].Friend, userpresence.agentData.AgentID); 85 lock (m_presences)
97
98 if (m_presences.Contains(uFriendList[i].Friend))
99 { 86 {
100 UserPresenceData friendup = (UserPresenceData)m_presences[uFriendList[i].Friend]; 87 m_presences.TryGetValue(pair.Key, out friendup);
101 // Add backreference 88 }
102 89 if (friendup != null)
103 SubscribeToPresenceUpdates(userpresence, friendup, uFriendList[i],i); 90 {
91 SubscribeToPresenceUpdates(userpresence, friendup, pair.Value);
104 } 92 }
105 } 93 }
106 } 94 }
107 95
108 /// <summary> 96 /// <summary>
109 /// Does the necessary work to subscribe one agent to another's presence notifications 97 /// Enqueues a presence update, sending info about user 'talkingAbout' to user 'receiver'.
110 /// Gets called by ProcessFriendListSubscriptions. You shouldn't call this directly
111 /// unless you know what you're doing
112 /// </summary> 98 /// </summary>
113 /// <param name="userpresence">P1</param> 99 /// <param name="talkingAbout">We are sending presence information about this user.</param>
114 /// <param name="friendpresence">P2</param> 100 /// <param name="receiver">We are sending the presence update to this user</param>
115 /// <param name="uFriendListItem"></param> 101 private void enqueuePresenceUpdate(UserPresenceData talkingAbout, UserPresenceData receiver)
116 /// <param name="uFriendListIndex"></param>
117 public void SubscribeToPresenceUpdates(UserPresenceData userpresence, UserPresenceData friendpresence,
118 FriendListItem uFriendListItem, int uFriendListIndex)
119 { 102 {
120 if ((uFriendListItem.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0) 103 UserAgentData p2Handle = m_userManager.GetUserAgentData(receiver.agentData.AgentID);
104 if (p2Handle != null)
121 { 105 {
122 // Subscribe and Send Out updates 106 if (receiver.lookupUserRegionYN)
123 if (!friendpresence.subscriptionData.Contains(friendpresence.agentData.AgentID))
124 {
125 userpresence.subscriptionData.Add(friendpresence.agentData.AgentID);
126 //Send Region Notice....
127 }
128 else
129 { 107 {
130 // we need to send out online status update, but the user is already subscribed 108 receiver.regionData.regionHandle = p2Handle.Handle;
131 }
132 UserAgentData p2Handle = m_userManager.GetUserAgentData(userpresence.agentData.AgentID);
133 if (p2Handle != null)
134 {
135 if (userpresence.lookupUserRegionYN)
136 {
137 userpresence.regionData.regionHandle = p2Handle.Handle;
138 }
139 else
140 {
141 userpresence.lookupUserRegionYN = true;
142 }
143 PresenceInformer friendlistupdater = new PresenceInformer();
144 friendlistupdater.presence1 = friendpresence;
145 //friendlistupdater.gridserverurl = m_cfg.GridServerURL;
146 //friendlistupdater.gridserversendkey = m_cfg.GridSendKey;
147 //friendlistupdater.gridserverrecvkey = m_cfg.GridRecvKey;
148 friendlistupdater.presence2 = userpresence;
149 friendlistupdater.OnGetRegionData += GetRegionInfo;
150 friendlistupdater.OnDone += PresenceUpdateDone;
151 WaitCallback cb = new WaitCallback(friendlistupdater.go);
152 ThreadPool.QueueUserWorkItem(cb);
153
154 } 109 }
155 else 110 else
156 { 111 {
157 // Skip because we can't find any data on the user 112 receiver.lookupUserRegionYN = true; // TODO Huh?
158 } 113 }
159 114
160 //SendRegionPresenceUpdate(friendpresence, userpresence); 115 PresenceInformer friendlistupdater = new PresenceInformer();
116 friendlistupdater.presence1 = talkingAbout;
117 friendlistupdater.presence2 = receiver;
118 friendlistupdater.OnGetRegionData += GetRegionInfo;
119 friendlistupdater.OnDone += PresenceUpdateDone;
120 WaitCallback cb = new WaitCallback(friendlistupdater.go);
121 ThreadPool.QueueUserWorkItem(cb);
161 } 122 }
162 123 else
163 if ((uFriendListItem.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0)
164 { 124 {
165 if (!friendpresence.subscriptionData.Contains(userpresence.agentData.AgentID)) 125 m_log.WarnFormat("no data found for user {0}", receiver.agentData.AgentID);
166 { 126 // Skip because we can't find any data on the user
167 friendpresence.subscriptionData.Add(userpresence.agentData.AgentID);
168 //Send Region Notice....
169 }
170 else
171 {
172 // we need to send out online status update, but the user is already subscribed
173 }
174
175 UserAgentData p2Handle = m_userManager.GetUserAgentData(friendpresence.agentData.AgentID);
176
177 if (p2Handle != null)
178 {
179
180 friendpresence.regionData.regionHandle = p2Handle.Handle;
181 PresenceInformer friendlistupdater = new PresenceInformer();
182 friendlistupdater.presence1 = userpresence;
183 friendlistupdater.presence2 = friendpresence;
184 //friendlistupdater.gridserverurl = m_cfg.GridServerURL;
185 //friendlistupdater.gridserversendkey = m_cfg.GridSendKey;
186 //friendlistupdater.gridserverrecvkey = m_cfg.GridRecvKey;
187 friendlistupdater.OnGetRegionData += GetRegionInfo;
188 friendlistupdater.OnDone += PresenceUpdateDone;
189 WaitCallback cb2 = new WaitCallback(friendlistupdater.go);
190 ThreadPool.QueueUserWorkItem(cb2);
191 }
192 else
193 {
194 // skip, agent doesn't appear to exist anymore
195 }
196
197
198
199 //SendRegionPresenceUpdate(userpresence, friendpresence);
200 } 127 }
201 } 128 }
202 129
203 /// <summary> 130 /// <summary>
204 /// Adds a backreference so presence specific data doesn't have to be 131 /// Does the necessary work to subscribe one agent to another's presence notifications
205 /// enumerated for each logged in user every time someone logs on or off. 132 /// Gets called by ProcessFriendListSubscriptions. You shouldn't call this directly
133 /// unless you know what you're doing
206 /// </summary> 134 /// </summary>
207 /// <param name="agentID"></param> 135 /// <param name="userpresence">P1</param>
208 /// <param name="friendID"></param> 136 /// <param name="friendpresence">P2</param>
209 public void addBackReference(UUID agentID, UUID friendID) 137 /// <param name="uFriendListItem"></param>
138 private void SubscribeToPresenceUpdates(UserPresenceData userpresence,
139 UserPresenceData friendpresence,
140 FriendListItem uFriendListItem)
210 { 141 {
211 if (m_presence_BackReferences.Contains(friendID)) 142 // Can the friend see me online?
143 if ((uFriendListItem.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0)
212 { 144 {
213 List<UUID> presenseBackReferences = (List<UUID>)m_presence_BackReferences[friendID]; 145 // tell user to update friend about user's presence changes
214 if (!presenseBackReferences.Contains(agentID)) 146 if (!userpresence.subscriptionData.Contains(friendpresence.agentData.AgentID))
215 { 147 {
216 presenseBackReferences.Add(agentID); 148 userpresence.subscriptionData.Add(friendpresence.agentData.AgentID);
217 } 149 }
218 m_presence_BackReferences[friendID] = presenseBackReferences; 150
219 } 151 // send an update about user's presence to the friend
220 else 152 enqueuePresenceUpdate(userpresence, friendpresence);
221 {
222 List<UUID> presenceBackReferences = new List<UUID>();
223 presenceBackReferences.Add(agentID);
224 m_presence_BackReferences[friendID] = presenceBackReferences;
225 } 153 }
226 }
227 154
228 /// <summary> 155 // Can I see the friend online?
229 /// Removes a backreference to free up some memory 156 if ((uFriendListItem.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0)
230 /// </summary>
231 /// <param name="agentID"></param>
232 /// <param name="friendID"></param>
233 public void removeBackReference(UUID agentID, UUID friendID)
234 {
235 if (m_presence_BackReferences.Contains(friendID))
236 { 157 {
237 List<UUID> presenseBackReferences = (List<UUID>)m_presence_BackReferences[friendID]; 158 // tell friend to update user about friend's presence changes
238 if (presenseBackReferences.Contains(agentID)) 159 if (!friendpresence.subscriptionData.Contains(userpresence.agentData.AgentID))
239 { 160 {
240 presenseBackReferences.Remove(agentID); 161 friendpresence.subscriptionData.Add(userpresence.agentData.AgentID);
241 } 162 }
242 163
243 // If there are no more backreferences for this agent, 164 // send an update about friend's presence to user.
244 // remove it to free up memory. 165 enqueuePresenceUpdate(friendpresence, userpresence);
245 if (presenseBackReferences.Count == 0)
246 {
247 m_presence_BackReferences.Remove(agentID);
248 }
249 } 166 }
250 } 167 }
251 168
@@ -256,90 +173,42 @@ namespace OpenSim.Grid.MessagingServer
256 private void ProcessLogOff(UUID AgentID) 173 private void ProcessLogOff(UUID AgentID)
257 { 174 {
258 m_log.Info("[LOGOFF]: Processing Logoff"); 175 m_log.Info("[LOGOFF]: Processing Logoff");
259 UserPresenceData AgentData = null; 176
260 List<UUID> AgentsNeedingNotification = new List<UUID>(); 177 UserPresenceData userPresence = null;
261 UserPresenceData friendd = null;
262 lock (m_presences) 178 lock (m_presences)
263 { 179 {
264 if (m_presences.Contains(AgentID)) 180 m_presences.TryGetValue(AgentID, out userPresence);
265 {
266 AgentData = (UserPresenceData)m_presences[AgentID];
267 }
268 } 181 }
269 182
270 if (AgentData != null) 183 if (userPresence != null) // found the user
271 { 184 {
272 AgentsNeedingNotification = AgentData.subscriptionData; 185 List<UUID> AgentsNeedingNotification = userPresence.subscriptionData;
273 AgentData.OnlineYN = false; 186 userPresence.OnlineYN = false;
274 //lock (m_presence_BackReferences)
275 //{
276 //if (m_presence_BackReferences.Contains(AgentID))
277 //{
278 //AgentsNeedingNotification = (List<UUID>)m_presence_BackReferences[AgentID];
279 //}
280 //}
281 187
282 for (int i = 0; i < AgentsNeedingNotification.Count; i++) 188 for (int i = 0; i < AgentsNeedingNotification.Count; i++)
283 { 189 {
284 // TODO: Do Region Notifications 190 UserPresenceData friendPresence = null;
285 lock (m_presences) 191 lock (m_presences)
286 { 192 {
287 if (m_presences.Contains(AgentsNeedingNotification[i])) 193 m_presences.TryGetValue(AgentsNeedingNotification[i], out friendPresence);
288 {
289 friendd = (UserPresenceData)m_presences[AgentsNeedingNotification[i]];
290 }
291 } 194 }
292 195
293 // This might need to be enumerated and checked before we try to remove it. 196 // This might need to be enumerated and checked before we try to remove it.
294 if (friendd != null) 197 if (friendPresence != null)
295 { 198 {
296 lock (friendd) 199 lock (friendPresence)
297 { 200 {
298 friendd.subscriptionData.Remove(AgentID); 201 // no updates for this user anymore
202 friendPresence.subscriptionData.Remove(AgentID);
299 203
300 List<FriendListItem> fl = friendd.friendData; 204 // set user's entry in the friend's list to offline (if it exists)
301 for (int j = 0; j < fl.Count; j++) 205 if (friendPresence.friendData.ContainsKey(AgentID))
302 { 206 {
303 if (fl[j].Friend == AgentID) 207 friendPresence.friendData[AgentID].onlinestatus = false;
304 {
305 fl[j].onlinestatus = false;
306 }
307 } 208 }
308
309 friendd.friendData = fl;
310 m_presences[AgentsNeedingNotification[i]] = friendd;
311 }
312
313 UserAgentData p2Handle = m_userManager.GetUserAgentData(friendd.agentData.AgentID);
314 if (p2Handle != null)
315 {
316
317
318 friendd.regionData.regionHandle = p2Handle.Handle;
319 PresenceInformer friendlistupdater = new PresenceInformer();
320 friendlistupdater.presence1 = AgentData;
321 friendlistupdater.presence2 = friendd;
322
323 //friendlistupdater.gridserverurl = m_cfg.GridServerURL;
324 //friendlistupdater.gridserversendkey = m_cfg.GridSendKey;
325 //friendlistupdater.gridserverrecvkey = m_cfg.GridRecvKey;
326
327 friendlistupdater.OnGetRegionData += GetRegionInfo;
328 friendlistupdater.OnDone += PresenceUpdateDone;
329
330 WaitCallback cb3 = new WaitCallback(friendlistupdater.go);
331 ThreadPool.QueueUserWorkItem(cb3);
332
333
334
335 }
336 else
337 {
338 // skip, agent can't be found
339 } 209 }
340 //SendRegionPresenceUpdate(AgentData, friendd);
341 210
342 //removeBackReference(AgentID, AgentsNeedingNotification[i]); 211 enqueuePresenceUpdate(userPresence, friendPresence);
343 } 212 }
344 } 213 }
345 } 214 }
@@ -347,7 +216,7 @@ namespace OpenSim.Grid.MessagingServer
347 216
348 #endregion 217 #endregion
349 218
350 public void PresenceUpdateDone(PresenceInformer obj) 219 private void PresenceUpdateDone(PresenceInformer obj)
351 { 220 {
352 obj.OnGetRegionData -= GetRegionInfo; 221 obj.OnGetRegionData -= GetRegionInfo;
353 obj.OnDone -= PresenceUpdateDone; 222 obj.OnDone -= PresenceUpdateDone;
@@ -356,12 +225,13 @@ namespace OpenSim.Grid.MessagingServer
356 #region UserServer Comms 225 #region UserServer Comms
357 226
358 /// <summary> 227 /// <summary>
359 /// Returns a list of FriendsListItems that describe the friends and permissions in the friend relationship for UUID friendslistowner 228 /// Returns a list of FriendsListItems that describe the friends and permissions in the friend
229 /// relationship for UUID friendslistowner. For faster lookup, we index by friend's UUID.
360 /// </summary> 230 /// </summary>
361 /// <param name="friendlistowner">The agent that we're retreiving the friends Data.</param> 231 /// <param name="friendlistowner">The agent that we're retreiving the friends Data for.</param>
362 public List<FriendListItem> GetUserFriendList(UUID friendlistowner) 232 private Dictionary<UUID, FriendListItem> GetUserFriendList(UUID friendlistowner)
363 { 233 {
364 List<FriendListItem> buddylist = new List<FriendListItem>(); 234 Dictionary<UUID, FriendListItem> buddies = new Dictionary<UUID,FriendListItem>();
365 235
366 try 236 try
367 { 237 {
@@ -376,7 +246,7 @@ namespace OpenSim.Grid.MessagingServer
376 246
377 if (respData.Contains("avcount")) 247 if (respData.Contains("avcount"))
378 { 248 {
379 buddylist = ConvertXMLRPCDataToFriendListItemList(respData); 249 buddies = ConvertXMLRPCDataToFriendListItemList(respData);
380 } 250 }
381 251
382 } 252 }
@@ -386,7 +256,7 @@ namespace OpenSim.Grid.MessagingServer
386 e.Message); 256 e.Message);
387 // Return Empty list (no friends) 257 // Return Empty list (no friends)
388 } 258 }
389 return buddylist; 259 return buddies;
390 } 260 }
391 261
392 /// <summary> 262 /// <summary>
@@ -394,9 +264,9 @@ namespace OpenSim.Grid.MessagingServer
394 /// </summary> 264 /// </summary>
395 /// <param name="data">XMLRPC response data Hashtable</param> 265 /// <param name="data">XMLRPC response data Hashtable</param>
396 /// <returns></returns> 266 /// <returns></returns>
397 public List<FriendListItem> ConvertXMLRPCDataToFriendListItemList(Hashtable data) 267 public Dictionary<UUID, FriendListItem> ConvertXMLRPCDataToFriendListItemList(Hashtable data)
398 { 268 {
399 List<FriendListItem> buddylist = new List<FriendListItem>(); 269 Dictionary<UUID, FriendListItem> buddies = new Dictionary<UUID,FriendListItem>();
400 int buddycount = Convert.ToInt32((string)data["avcount"]); 270 int buddycount = Convert.ToInt32((string)data["avcount"]);
401 271
402 for (int i = 0; i < buddycount; i++) 272 for (int i = 0; i < buddycount; i++)
@@ -408,10 +278,10 @@ namespace OpenSim.Grid.MessagingServer
408 buddylistitem.FriendListOwnerPerms = (uint)Convert.ToInt32((string)data["ownerPerms" + i.ToString()]); 278 buddylistitem.FriendListOwnerPerms = (uint)Convert.ToInt32((string)data["ownerPerms" + i.ToString()]);
409 buddylistitem.FriendPerms = (uint)Convert.ToInt32((string)data["friendPerms" + i.ToString()]); 279 buddylistitem.FriendPerms = (uint)Convert.ToInt32((string)data["friendPerms" + i.ToString()]);
410 280
411 buddylist.Add(buddylistitem); 281 buddies.Add(buddylistitem.Friend, buddylistitem);
412 } 282 }
413 283
414 return buddylist; 284 return buddies;
415 } 285 }
416 286
417 /// <summary> 287 /// <summary>
@@ -423,20 +293,8 @@ namespace OpenSim.Grid.MessagingServer
423 /// <returns></returns> 293 /// <returns></returns>
424 public XmlRpcResponse UserLoggedOn(XmlRpcRequest request) 294 public XmlRpcResponse UserLoggedOn(XmlRpcRequest request)
425 { 295 {
426 m_log.Info("[LOGON]: User logged on, building indexes for user");
427 Hashtable requestData = (Hashtable)request.Params[0]; 296 Hashtable requestData = (Hashtable)request.Params[0];
428 297
429 //requestData["sendkey"] = serv.sendkey;
430 //requestData["agentid"] = agentID.ToString();
431 //requestData["sessionid"] = sessionID.ToString();
432 //requestData["regionid"] = RegionID.ToString();
433 //requestData["regionhandle"] = regionhandle.ToString();
434 //requestData["positionx"] = positionX.ToString();
435 //requestData["positiony"] = positionY.ToString();
436 //requestData["positionz"] = positionZ.ToString();
437 //requestData["firstname"] = firstname;
438 //requestData["lastname"] = lastname;
439
440 AgentCircuitData agentData = new AgentCircuitData(); 298 AgentCircuitData agentData = new AgentCircuitData();
441 agentData.SessionID = new UUID((string)requestData["sessionid"]); 299 agentData.SessionID = new UUID((string)requestData["sessionid"]);
442 agentData.SecureSessionID = new UUID((string)requestData["secure_session_id"]); 300 agentData.SecureSessionID = new UUID((string)requestData["secure_session_id"]);
@@ -461,12 +319,13 @@ namespace OpenSim.Grid.MessagingServer
461 319
462 ulong regionHandle = Convert.ToUInt64((string)requestData["regionhandle"]); 320 ulong regionHandle = Convert.ToUInt64((string)requestData["regionhandle"]);
463 321
322 m_log.InfoFormat("[LOGON]: User {0} {1} logged into region {2} as {3} agent, building indexes for user",
323 agentData.firstname, agentData.lastname, regionHandle, agentData.child ? "child" : "root");
324
464 UserPresenceData up = new UserPresenceData(); 325 UserPresenceData up = new UserPresenceData();
465 up.agentData = agentData; 326 up.agentData = agentData;
466 List<FriendListItem> flData = GetUserFriendList(agentData.AgentID); 327 up.friendData = GetUserFriendList(agentData.AgentID);
467 up.friendData = flData; 328 up.regionData = GetRegionInfo(regionHandle);
468 RegionProfileData riData = GetRegionInfo(regionHandle);
469 up.regionData = riData;
470 up.OnlineYN = true; 329 up.OnlineYN = true;
471 up.lookupUserRegionYN = false; 330 up.lookupUserRegionYN = false;
472 ProcessFriendListSubscriptions(up); 331 ProcessFriendListSubscriptions(up);
@@ -486,7 +345,6 @@ namespace OpenSim.Grid.MessagingServer
486 Hashtable requestData = (Hashtable)request.Params[0]; 345 Hashtable requestData = (Hashtable)request.Params[0];
487 346
488 UUID AgentID = new UUID((string)requestData["agentid"]); 347 UUID AgentID = new UUID((string)requestData["agentid"]);
489
490 ProcessLogOff(AgentID); 348 ProcessLogOff(AgentID);
491 349
492 return new XmlRpcResponse(); 350 return new XmlRpcResponse();
@@ -494,6 +352,49 @@ namespace OpenSim.Grid.MessagingServer
494 352
495 #endregion 353 #endregion
496 354
355 public XmlRpcResponse GetPresenceInfoBulk(XmlRpcRequest request)
356 {
357 Hashtable paramHash = (Hashtable)request.Params[0];
358 Hashtable result = new Hashtable();
359
360 // TODO check access (recv_key/send_key)
361
362 IList list = (IList)paramHash["uuids"];
363
364 // convert into List<UUID>
365 List<UUID> uuids = new List<UUID>();
366 for (int i = 0; i < list.Count; ++i)
367 {
368 UUID uuid;
369 if (UUID.TryParse((string)list[i], out uuid))
370 {
371 uuids.Add(uuid);
372 }
373 }
374
375 try {
376 Dictionary<UUID, FriendRegionInfo> infos = m_userManager.GetFriendRegionInfos(uuids);
377 m_log.DebugFormat("[FRIEND]: Got {0} region entries back.", infos.Count);
378 int count = 0;
379 foreach (KeyValuePair<UUID, FriendRegionInfo> pair in infos)
380 {
381 result["uuid_" + count] = pair.Key.ToString();
382 result["isOnline_" + count] = pair.Value.isOnline;
383 result["regionHandle_" + count] = pair.Value.regionHandle.ToString(); // XML-RPC doesn't know ulongs
384 ++count;
385 }
386 result["count"] = count;
387
388 XmlRpcResponse response = new XmlRpcResponse();
389 response.Value = result;
390 return response;
391 }
392 catch(Exception e) {
393 m_log.Error("Got exception:", e);
394 throw e;
395 }
396 }
397
497 #region regioninfo gathering 398 #region regioninfo gathering
498 399
499 /// <summary> 400 /// <summary>
@@ -506,37 +407,21 @@ namespace OpenSim.Grid.MessagingServer
506 public RegionProfileData GetRegionInfo(ulong regionhandle) 407 public RegionProfileData GetRegionInfo(ulong regionhandle)
507 { 408 {
508 RegionProfileData regionInfo = null; 409 RegionProfileData regionInfo = null;
509 bool lookup = false;
510 410
511 lock (m_regionInfoCache) 411 lock (m_regionInfoCache)
512 { 412 {
513 if (m_regionInfoCache.Contains(regionhandle)) 413 m_regionInfoCache.TryGetValue(regionhandle, out regionInfo);
514 {
515 regionInfo = (RegionProfileData)m_regionInfoCache[regionhandle];
516 }
517 else
518 {
519 // Don't lock the cache while we're looking up the region!
520 lookup = true;
521 }
522 } 414 }
523 415
524 if (lookup) 416 if (regionInfo == null) // not found in cache
525 { 417 {
526 regionInfo = RequestRegionInfo(regionhandle); 418 regionInfo = RequestRegionInfo(regionhandle);
527 419
528 if (regionInfo != null) 420 if (regionInfo != null) // lookup was successful
529 { 421 {
530 lock (m_regionInfoCache) 422 lock (m_regionInfoCache)
531 { 423 {
532 if (m_regionInfoCache.Contains(regionhandle)) 424 m_regionInfoCache[regionhandle] = regionInfo;
533 {
534 m_regionInfoCache[regionhandle] = regionInfo;
535 }
536 else
537 {
538 m_regionInfoCache.Add(regionhandle, regionInfo);
539 }
540 } 425 }
541 } 426 }
542 } 427 }
@@ -558,21 +443,25 @@ namespace OpenSim.Grid.MessagingServer
558 } 443 }
559 444
560 /// <summary> 445 /// <summary>
561 /// Get RegionProfileData from the GridServer 446 /// Get RegionProfileData from the GridServer.
562 /// We'll Cache this information and use it for presence updates 447 /// We'll cache this information in GetRegionInfo and use it for presence updates
563 /// </summary> 448 /// </summary>
564 /// <param name="regionHandle"></param> 449 /// <param name="regionHandle"></param>
565 /// <returns></returns> 450 /// <returns></returns>
566 public RegionProfileData RequestRegionInfo(ulong regionHandle) 451 public RegionProfileData RequestRegionInfo(ulong regionHandle)
567 { RegionProfileData regionProfile = null; 452 {
453 RegionProfileData regionProfile = null;
568 try 454 try
569 { 455 {
570 Hashtable requestData = new Hashtable(); 456 Hashtable requestData = new Hashtable();
571 requestData["region_handle"] = regionHandle.ToString(); 457 requestData["region_handle"] = regionHandle.ToString();
572 requestData["authkey"] = m_cfg.GridSendKey; 458 requestData["authkey"] = m_cfg.GridSendKey;
459
573 ArrayList SendParams = new ArrayList(); 460 ArrayList SendParams = new ArrayList();
574 SendParams.Add(requestData); 461 SendParams.Add(requestData);
462
575 XmlRpcRequest GridReq = new XmlRpcRequest("simulator_data_request", SendParams); 463 XmlRpcRequest GridReq = new XmlRpcRequest("simulator_data_request", SendParams);
464
576 XmlRpcResponse GridResp = GridReq.Send(m_cfg.GridServerURL, 3000); 465 XmlRpcResponse GridResp = GridReq.Send(m_cfg.GridServerURL, 3000);
577 466
578 Hashtable responseData = (Hashtable)GridResp.Value; 467 Hashtable responseData = (Hashtable)GridResp.Value;
@@ -586,9 +475,6 @@ namespace OpenSim.Grid.MessagingServer
586 uint regX = Convert.ToUInt32((string)responseData["region_locx"]); 475 uint regX = Convert.ToUInt32((string)responseData["region_locx"]);
587 uint regY = Convert.ToUInt32((string)responseData["region_locy"]); 476 uint regY = Convert.ToUInt32((string)responseData["region_locy"]);
588 string internalIpStr = (string)responseData["sim_ip"]; 477 string internalIpStr = (string)responseData["sim_ip"];
589 // uint port = Convert.ToUInt32(responseData["sim_port"]);
590 // string externalUri = (string)responseData["sim_uri"];
591 // string neighbourExternalUri = externalUri;
592 478
593 regionProfile = new RegionProfileData(); 479 regionProfile = new RegionProfileData();
594 regionProfile.httpPort = (uint)Convert.ToInt32((string)responseData["http_port"]); 480 regionProfile.httpPort = (uint)Convert.ToInt32((string)responseData["http_port"]);
@@ -600,20 +486,12 @@ namespace OpenSim.Grid.MessagingServer
600 regionProfile.remotingPort = Convert.ToUInt32((string)responseData["remoting_port"]); 486 regionProfile.remotingPort = Convert.ToUInt32((string)responseData["remoting_port"]);
601 regionProfile.UUID = new UUID((string)responseData["region_UUID"]); 487 regionProfile.UUID = new UUID((string)responseData["region_UUID"]);
602 regionProfile.regionName = (string)responseData["region_name"]; 488 regionProfile.regionName = (string)responseData["region_name"];
603 lock (m_regionInfoCache)
604 {
605 if (!m_regionInfoCache.Contains(regionHandle))
606 {
607 m_regionInfoCache.Add(regionHandle, regionProfile);
608 }
609 }
610 } 489 }
611 catch (WebException) 490 catch (WebException)
612 { 491 {
613 m_log.Error("[GRID]: " + 492 m_log.Error("[GRID]: " +
614 "Region lookup failed for: " + regionHandle.ToString() + 493 "Region lookup failed for: " + regionHandle.ToString() +
615 " - Is the GridServer down?"); 494 " - Is the GridServer down?");
616 return null;
617 } 495 }
618 496
619 return regionProfile; 497 return regionProfile;
@@ -641,29 +519,20 @@ namespace OpenSim.Grid.MessagingServer
641 SendParams.Add(UserParams); 519 SendParams.Add(UserParams);
642 520
643 // Send Request 521 // Send Request
644 XmlRpcRequest UserReq;
645 XmlRpcResponse UserResp;
646 try 522 try
647 { 523 {
648 UserReq = new XmlRpcRequest("register_messageserver", SendParams); 524 XmlRpcRequest UserReq = new XmlRpcRequest("register_messageserver", SendParams);
649 UserResp = UserReq.Send(m_cfg.UserServerURL, 16000); 525 XmlRpcResponse UserResp = UserReq.Send(m_cfg.UserServerURL, 16000);
650 } catch (Exception ex)
651 {
652 m_log.Error("Unable to connect to grid. Grid server not running?");
653 throw(ex);
654 }
655 Hashtable GridRespData = (Hashtable)UserResp.Value;
656 // Hashtable griddatahash = GridRespData;
657
658 // Process Response
659 if (GridRespData.ContainsKey("responsestring"))
660 {
661 526
662 return true; 527 // Process Response
528 Hashtable GridRespData = (Hashtable)UserResp.Value;
529 // if we got a response, we were successful
530 return GridRespData.ContainsKey("responsestring");
663 } 531 }
664 else 532 catch (Exception ex)
665 { 533 {
666 return false; 534 m_log.Error("Unable to connect to grid. User server not running?");
535 throw(ex);
667 } 536 }
668 } 537 }
669 538
@@ -689,30 +558,20 @@ namespace OpenSim.Grid.MessagingServer
689 SendParams.Add(UserParams); 558 SendParams.Add(UserParams);
690 559
691 // Send Request 560 // Send Request
692 XmlRpcRequest UserReq;
693 XmlRpcResponse UserResp;
694 try 561 try
695 { 562 {
696 UserReq = new XmlRpcRequest("deregister_messageserver", SendParams); 563 XmlRpcRequest UserReq = new XmlRpcRequest("deregister_messageserver", SendParams);
697 UserResp = UserReq.Send(m_cfg.UserServerURL, 16000); 564 XmlRpcResponse UserResp = UserReq.Send(m_cfg.UserServerURL, 16000);
565 // Process Response
566 Hashtable UserRespData = (Hashtable)UserResp.Value;
567 // if we got a response, we were successful
568 return UserRespData.ContainsKey("responsestring");
698 } 569 }
699 catch (Exception ex) 570 catch (Exception ex)
700 { 571 {
701 m_log.Error("Unable to connect to grid. Grid server not running?"); 572 m_log.Error("Unable to connect to grid. User server not running?");
702 throw (ex); 573 throw (ex);
703 } 574 }
704 Hashtable UserRespData = (Hashtable)UserResp.Value;
705 // Hashtable userdatahash = UserRespData;
706
707 // Process Response
708 if (UserRespData.ContainsKey("responsestring"))
709 {
710 return true;
711 }
712 else
713 {
714 return false;
715 }
716 } 575 }
717 576
718 #endregion 577 #endregion
diff --git a/OpenSim/Grid/MessagingServer/UserPresenceData.cs b/OpenSim/Grid/MessagingServer/UserPresenceData.cs
index 2119c13..d548fc6 100644
--- a/OpenSim/Grid/MessagingServer/UserPresenceData.cs
+++ b/OpenSim/Grid/MessagingServer/UserPresenceData.cs
@@ -38,7 +38,7 @@ namespace OpenSim.Grid.MessagingServer
38 public AgentCircuitData agentData = new AgentCircuitData(); 38 public AgentCircuitData agentData = new AgentCircuitData();
39 public RegionProfileData regionData = new RegionProfileData(); 39 public RegionProfileData regionData = new RegionProfileData();
40 public string httpURI = String.Empty; 40 public string httpURI = String.Empty;
41 public List<FriendListItem> friendData = new List<FriendListItem> (); 41 public Dictionary<UUID, FriendListItem> friendData = new Dictionary<UUID,FriendListItem>();
42 public List<UUID> subscriptionData = new List<UUID>(); 42 public List<UUID> subscriptionData = new List<UUID>();
43 public bool OnlineYN = true; 43 public bool OnlineYN = true;
44 public bool lookupUserRegionYN = true; 44 public bool lookupUserRegionYN = true;
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index 4f1eed8..2bb4b57 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -364,7 +364,8 @@ namespace OpenSim
364 m_commsManager 364 m_commsManager
365 = new CommunicationsLocal( 365 = new CommunicationsLocal(
366 m_networkServersInfo, m_httpServer, m_assetCache, userService, userService, 366 m_networkServersInfo, m_httpServer, m_assetCache, userService, userService,
367 inventoryService, backendService, backendService, libraryRootFolder, m_dumpAssetsToFile); 367 inventoryService, backendService, backendService, userService,
368 libraryRootFolder, m_dumpAssetsToFile);
368 369
369 // set up XMLRPC handler for client's initial login request message 370 // set up XMLRPC handler for client's initial login request message
370 m_httpServer.AddXmlRPCHandler("login_to_simulator", loginService.XmlRpcLoginMethod); 371 m_httpServer.AddXmlRPCHandler("login_to_simulator", loginService.XmlRpcLoginMethod);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 5b3bc98..363b09c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -4129,7 +4129,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4129 handlerApproveFriendRequest(this, agentID, transactionID, callingCardFolders); 4129 handlerApproveFriendRequest(this, agentID, transactionID, callingCardFolders);
4130 } 4130 }
4131 break; 4131 break;
4132 4132
4133 case PacketType.DeclineFriendship:
4134 DeclineFriendshipPacket dfriendpack = (DeclineFriendshipPacket)Pack;
4135
4136 if (OnDenyFriendRequest != null)
4137 {
4138 OnDenyFriendRequest(this,
4139 dfriendpack.AgentData.AgentID,
4140 dfriendpack.TransactionBlock.TransactionID,
4141 null);
4142 }
4143 break;
4144
4133 case PacketType.TerminateFriendship: 4145 case PacketType.TerminateFriendship:
4134 TerminateFriendshipPacket tfriendpack = (TerminateFriendshipPacket)Pack; 4146 TerminateFriendshipPacket tfriendpack = (TerminateFriendshipPacket)Pack;
4135 UUID listOwnerAgentID = tfriendpack.AgentData.AgentID; 4147 UUID listOwnerAgentID = tfriendpack.AgentData.AgentID;
@@ -7648,6 +7660,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7648 OutPacket(p, ThrottleOutPacketType.Task); 7660 OutPacket(p, ThrottleOutPacketType.Task);
7649 } 7661 }
7650 7662
7663 public void SendTerminateFriend(UUID exFriendID)
7664 {
7665 TerminateFriendshipPacket p = (TerminateFriendshipPacket)PacketPool.Instance.GetPacket(PacketType.TerminateFriendship);
7666 p.AgentData.AgentID = AgentId;
7667 p.AgentData.SessionID = SessionId;
7668 p.ExBlock.OtherID = exFriendID;
7669 OutPacket(p, ThrottleOutPacketType.Task);
7670 }
7671
7651 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data) 7672 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data)
7652 { 7673 {
7653 int i; 7674 int i;
diff --git a/OpenSim/Region/Communications/Local/CommunicationsLocal.cs b/OpenSim/Region/Communications/Local/CommunicationsLocal.cs
index 71c44e5..48a635a 100644
--- a/OpenSim/Region/Communications/Local/CommunicationsLocal.cs
+++ b/OpenSim/Region/Communications/Local/CommunicationsLocal.cs
@@ -42,7 +42,8 @@ namespace OpenSim.Region.Communications.Local
42 IUserServiceAdmin userServiceAdmin, 42 IUserServiceAdmin userServiceAdmin,
43 LocalInventoryService inventoryService, 43 LocalInventoryService inventoryService,
44 IInterRegionCommunications interRegionService, 44 IInterRegionCommunications interRegionService,
45 IGridServices gridService, LibraryRootFolder libraryRootFolder, bool dumpAssetsToFile) 45 IGridServices gridService, IMessagingService messageService,
46 LibraryRootFolder libraryRootFolder, bool dumpAssetsToFile)
46 : base(serversInfo, httpServer, assetCache, dumpAssetsToFile, libraryRootFolder) 47 : base(serversInfo, httpServer, assetCache, dumpAssetsToFile, libraryRootFolder)
47 { 48 {
48 AddInventoryService(inventoryService); 49 AddInventoryService(inventoryService);
@@ -53,6 +54,7 @@ namespace OpenSim.Region.Communications.Local
53 m_avatarService = (IAvatarService)userService; 54 m_avatarService = (IAvatarService)userService;
54 m_gridService = gridService; 55 m_gridService = gridService;
55 m_interRegion = interRegionService; 56 m_interRegion = interRegionService;
57 m_messageService = messageService;
56 } 58 }
57 } 59 }
58} 60}
diff --git a/OpenSim/Region/Communications/Local/LocalBackEndServices.cs b/OpenSim/Region/Communications/Local/LocalBackEndServices.cs
index 9034e49..4980378 100644
--- a/OpenSim/Region/Communications/Local/LocalBackEndServices.cs
+++ b/OpenSim/Region/Communications/Local/LocalBackEndServices.cs
@@ -39,7 +39,7 @@ namespace OpenSim.Region.Communications.Local
39 public class LocalBackEndServices : IGridServices, IInterRegionCommunications 39 public class LocalBackEndServices : IGridServices, IInterRegionCommunications
40 { 40 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 42
43 protected Dictionary<ulong, RegionInfo> m_regions = new Dictionary<ulong, RegionInfo>(); 43 protected Dictionary<ulong, RegionInfo> m_regions = new Dictionary<ulong, RegionInfo>();
44 44
45 protected Dictionary<ulong, RegionCommsListener> m_regionListeners = 45 protected Dictionary<ulong, RegionCommsListener> m_regionListeners =
@@ -50,7 +50,7 @@ namespace OpenSim.Region.Communications.Local
50 private Dictionary<string, string> m_queuedGridSettings = new Dictionary<string, string>(); 50 private Dictionary<string, string> m_queuedGridSettings = new Dictionary<string, string>();
51 51
52 public string _gdebugRegionName = String.Empty; 52 public string _gdebugRegionName = String.Empty;
53 53
54 public bool RegionLoginsEnabled 54 public bool RegionLoginsEnabled
55 { 55 {
56 get { return m_regionLoginsEnabled; } 56 get { return m_regionLoginsEnabled; }
@@ -523,11 +523,27 @@ namespace OpenSim.Region.Communications.Local
523 if (info.RegionName.StartsWith(name)) 523 if (info.RegionName.StartsWith(name))
524 { 524 {
525 regions.Add(info); 525 regions.Add(info);
526 if (regions.Count >= maxNumber) break; 526 if (regions.Count >= maxNumber) break;
527 } 527 }
528 } 528 }
529 529
530 return regions; 530 return regions;
531 } 531 }
532
533 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
534 {
535 // if we get to here, something is wrong: We are in standalone mode, but have users that are not on our server?
536 m_log.WarnFormat("[INTERREGION STANDALONE] Did find {0} users on a region not on our server: {1} ???",
537 friends.Count, destRegionHandle);
538 return new List<UUID>();
539 }
540
541 public bool TriggerTerminateFriend (ulong regionHandle, UUID agentID, UUID exFriendID)
542 {
543 // if we get to here, something is wrong: We are in standalone mode, but have users that are not on our server?
544 m_log.WarnFormat("[INTERREGION STANDALONE] Did find user {0} on a region not on our server: {1} ???",
545 agentID, regionHandle);
546 return true;
547 }
532 } 548 }
533} 549}
diff --git a/OpenSim/Region/Communications/OGS1/CommunicationsOGS1.cs b/OpenSim/Region/Communications/OGS1/CommunicationsOGS1.cs
index d76f076..7f6fbc8 100644
--- a/OpenSim/Region/Communications/OGS1/CommunicationsOGS1.cs
+++ b/OpenSim/Region/Communications/OGS1/CommunicationsOGS1.cs
@@ -56,7 +56,9 @@ namespace OpenSim.Region.Communications.OGS1
56 m_defaultInventoryHost = invService.Host; 56 m_defaultInventoryHost = invService.Host;
57 } 57 }
58 58
59 m_userService = new OGS1UserServices(this); 59 OGS1UserServices userServices = new OGS1UserServices(this);
60 m_userService = userServices;
61 m_messageService = userServices;
60 m_avatarService = (IAvatarService)m_userService; 62 m_avatarService = (IAvatarService)m_userService;
61 } 63 }
62 64
diff --git a/OpenSim/Region/Communications/OGS1/OGS1GridServices.cs b/OpenSim/Region/Communications/OGS1/OGS1GridServices.cs
index 32628f8..d9e0370 100644
--- a/OpenSim/Region/Communications/OGS1/OGS1GridServices.cs
+++ b/OpenSim/Region/Communications/OGS1/OGS1GridServices.cs
@@ -1785,5 +1785,83 @@ namespace OpenSim.Region.Communications.OGS1
1785 return null; 1785 return null;
1786 } 1786 }
1787 } 1787 }
1788
1789 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
1790 {
1791 List<UUID> tpdAway = new List<UUID>();
1792
1793 // destRegionHandle is a region on another server
1794 RegionInfo info = RequestNeighbourInfo(destRegionHandle);
1795 if (info != null)
1796 {
1797 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk";
1798
1799 Hashtable reqParams = new Hashtable();
1800 reqParams["agentID"] = agentId.ToString();
1801 reqParams["agentOnline"] = online;
1802 int count = 0;
1803 foreach (UUID uuid in friends)
1804 {
1805 reqParams["friendID_" + count++] = uuid.ToString();
1806 }
1807 reqParams["friendCount"] = count;
1808
1809 IList parameters = new ArrayList();
1810 parameters.Add(reqParams);
1811 try
1812 {
1813 XmlRpcRequest request = new XmlRpcRequest("presence_update_bulk", parameters);
1814 XmlRpcResponse response = request.Send(httpServer, 5000);
1815 Hashtable respData = (Hashtable)response.Value;
1816
1817 count = (int)respData["friendCount"];
1818 for (int i = 0; i < count; ++i)
1819 {
1820 UUID uuid;
1821 if(UUID.TryParse((string)respData["friendID_" + i], out uuid)) tpdAway.Add(uuid);
1822 }
1823 }
1824 catch(Exception e)
1825 {
1826 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
1827 }
1828 }
1829 else m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}???", destRegionHandle);
1830
1831 return tpdAway;
1832 }
1833
1834 public bool TriggerTerminateFriend(ulong destRegionHandle, UUID agentID, UUID exFriendID)
1835 {
1836 // destRegionHandle is a region on another server
1837 RegionInfo info = RequestNeighbourInfo(destRegionHandle);
1838 if (info == null)
1839 {
1840 m_log.WarnFormat("[OGS1 GRID SERVICES]: Couldn't find region {0}", destRegionHandle);
1841 return false; // region not found???
1842 }
1843
1844 string httpServer = "http://" + info.ExternalEndPoint.Address + ":" + info.HttpPort + "/presence_update_bulk";
1845
1846 Hashtable reqParams = new Hashtable();
1847 reqParams["agentID"] = agentID.ToString();
1848 reqParams["friendID"] = exFriendID.ToString();
1849
1850 IList parameters = new ArrayList();
1851 parameters.Add(reqParams);
1852 try
1853 {
1854 XmlRpcRequest request = new XmlRpcRequest("terminate_friend", parameters);
1855 XmlRpcResponse response = request.Send(httpServer, 5000);
1856 Hashtable respData = (Hashtable)response.Value;
1857
1858 return (bool)respData["success"];
1859 }
1860 catch(Exception e)
1861 {
1862 m_log.Error("[OGS1 GRID SERVICES]: InformFriendsInOtherRegion XMLRPC failure: ", e);
1863 return false;
1864 }
1865 }
1788 } 1866 }
1789} \ No newline at end of file 1867}
diff --git a/OpenSim/Region/Communications/OGS1/OGS1UserServices.cs b/OpenSim/Region/Communications/OGS1/OGS1UserServices.cs
index 28177d0..595c4a9 100644
--- a/OpenSim/Region/Communications/OGS1/OGS1UserServices.cs
+++ b/OpenSim/Region/Communications/OGS1/OGS1UserServices.cs
@@ -39,7 +39,7 @@ using OpenSim.Framework.Communications;
39 39
40namespace OpenSim.Region.Communications.OGS1 40namespace OpenSim.Region.Communications.OGS1
41{ 41{
42 public class OGS1UserServices : IUserService, IAvatarService 42 public class OGS1UserServices : IUserService, IAvatarService, IMessagingService
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
@@ -722,6 +722,64 @@ namespace OpenSim.Region.Communications.OGS1
722 return buddylist; 722 return buddylist;
723 } 723 }
724 724
725 public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos (List<UUID> uuids)
726 {
727 Dictionary<UUID, FriendRegionInfo> result = new Dictionary<UUID, FriendRegionInfo>();
728
729 // ask MessageServer about the current on-/offline status and regions the friends are in
730 ArrayList parameters = new ArrayList();
731 Hashtable map = new Hashtable();
732
733 ArrayList list = new ArrayList();
734 foreach (UUID uuid in uuids)
735 {
736 list.Add(uuid.ToString());
737 list.Add(uuid.ToString());
738 }
739 map["uuids"] = list;
740
741 map["recv_key"] = m_parent.NetworkServersInfo.UserRecvKey;
742 map["send_key"] = m_parent.NetworkServersInfo.UserRecvKey;
743
744 parameters.Add(map);
745
746 try {
747 XmlRpcRequest req = new XmlRpcRequest("get_presence_info_bulk", parameters);
748 XmlRpcResponse resp = req.Send(m_parent.NetworkServersInfo.MessagingURL, 8000);
749 Hashtable respData = (Hashtable) resp.Value;
750
751 if (respData.ContainsKey("faultMessage"))
752 {
753 m_log.WarnFormat("[OGS1 USER SERVICES]: Contacting MessageServer about user-regions resulted in error: {0}",
754 respData["faultMessage"]);
755 }
756 else
757 {
758 int count = (int)respData["count"];
759 m_log.DebugFormat("[OGS1 USER SERVICES]: Request returned {0} results.", count);
760 for (int i = 0; i < count; ++i)
761 {
762 UUID uuid;
763 if (UUID.TryParse((string)respData["uuid_" + i], out uuid))
764 {
765 FriendRegionInfo info = new FriendRegionInfo();
766 info.isOnline = (bool)respData["isOnline_" + i];
767 if (info.isOnline) info.regionHandle = Convert.ToUInt64(respData["regionHandle_" + i]);
768
769 result.Add(uuid, info);
770 }
771 }
772 }
773 }
774 catch (WebException e)
775 {
776 m_log.ErrorFormat("[OGS1 USER SERVICES]: Network problems when trying to fetch friend infos: {0}", e.Message);
777 }
778
779 m_log.DebugFormat("[OGS1 USER SERVICES]: Returning {0} entries", result.Count);
780 return result;
781 }
782
725 #endregion 783 #endregion
726 784
727 /// Appearance 785 /// Appearance
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
index a380700..e2cb2e0 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Friends/FriendsModule.cs
@@ -34,43 +34,100 @@ using log4net;
34using Nini.Config; 34using Nini.Config;
35using Nwc.XmlRpc; 35using Nwc.XmlRpc;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using OpenSim.Framework.Communications.Cache;
38using OpenSim.Framework.Servers;
37using OpenSim.Region.Environment.Interfaces; 39using OpenSim.Region.Environment.Interfaces;
38using OpenSim.Region.Environment.Scenes; 40using OpenSim.Region.Environment.Scenes;
39 41
40namespace OpenSim.Region.Environment.Modules.Avatar.Friends 42namespace OpenSim.Region.Environment.Modules.Avatar.Friends
41{ 43{
44 /*
45 This module handles adding/removing friends, and the the presence
46 notification process for login/logoff of friends.
47
48 The presence notification works as follows:
49 - After the user initially connects to a region (so we now have a UDP
50 connection to work with), this module fetches the friends of user
51 (those are cached), their on-/offline status, and info about the
52 region they are in from the MessageServer.
53 - (*) It then informs the user about the on-/offline status of her friends.
54 - It then informs all online friends currently on this region-server about
55 user's new online status (this will save some network traffic, as local
56 messages don't have to be transferred inter-region, and it will be all
57 that has to be done in Standalone Mode).
58 - For the rest of the online friends (those not on this region-server),
59 this module uses the provided region-information to map users to
60 regions, and sends one notification to every region containing the
61 friends to inform on that server.
62 - The region-server will handle that in the following way:
63 - If it finds the friend, it informs her about the user being online.
64 - If it doesn't find the friend (maybe she TPed away in the meantime),
65 it stores that information.
66 - After it processed all friends, it returns the list of friends it
67 couldn't find.
68 - If this list isn't empty, the FriendsModule re-requests information
69 about those online friends that have been missed and starts at (*)
70 again until all friends have been found, or until it tried 3 times
71 (to prevent endless loops due to some uncaught error).
72
73 NOTE: Online/Offline notifications don't need to be sent on region change.
74
75 We implement two XMLRpc handlers here, handling all the inter-region things
76 we have to handle:
77 - On-/Offline-Notifications (bulk)
78 - Terminate Friendship messages (single)
79 */
80
42 public class FriendsModule : IRegionModule 81 public class FriendsModule : IRegionModule
43 { 82 {
83 private class Transaction
84 {
85 public UUID agentID;
86 public string agentName;
87 public uint count;
88
89 public Transaction(UUID agentID, string agentName)
90 {
91 this.agentID = agentID;
92 this.agentName = agentName;
93 this.count = 1;
94 }
95 }
96
44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 97 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45 98
46 private Dictionary<UUID, List<FriendListItem>> FriendLists = new Dictionary<UUID, List<FriendListItem>>(); 99 private Cache m_friendLists = new Cache(CacheFlags.AllowUpdate);
47 private Dictionary<UUID, UUID> m_pendingFriendRequests = new Dictionary<UUID, UUID>(); 100
48 private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>(); 101 private Dictionary<UUID, ulong> m_rootAgents = new Dictionary<UUID, ulong>();
49 private Dictionary<UUID, List<StoredFriendListUpdate>> StoredFriendListUpdates = new Dictionary<UUID, List<StoredFriendListUpdate>>();
50 102
103 private Dictionary<UUID, Transaction> m_pendingFriendRequests = new Dictionary<UUID, Transaction>();
51 private Dictionary<UUID, UUID> m_pendingCallingcardRequests = new Dictionary<UUID,UUID>(); 104 private Dictionary<UUID, UUID> m_pendingCallingcardRequests = new Dictionary<UUID,UUID>();
52 105
53 private List<Scene> m_scene = new List<Scene>(); 106 private Scene m_initialScene; // saves a lookup if we don't have a specific scene
107 private Dictionary<ulong, Scene> m_scenes = new Dictionary<ulong,Scene>();
54 108
55 #region IRegionModule Members 109 #region IRegionModule Members
56 110
57 public void Initialise(Scene scene, IConfigSource config) 111 public void Initialise(Scene scene, IConfigSource config)
58 { 112 {
59 lock (m_scene) 113 lock (m_scenes)
60 { 114 {
61 if (m_scene.Count == 0) 115 if (m_scenes.Count == 0)
62 { 116 {
63 scene.AddXmlRPCHandler("presence_update", processPresenceUpdate); 117 scene.AddXmlRPCHandler("presence_update_bulk", processPresenceUpdateBulk);
118 scene.AddXmlRPCHandler("terminate_friend", processTerminateFriend);
119 m_friendLists.DefaultTTL = new TimeSpan(1, 0, 0); // store entries for one hour max
120 m_initialScene = scene;
64 } 121 }
65 122
66 if (!m_scene.Contains(scene)) 123 if (!m_scenes.ContainsKey(scene.RegionInfo.RegionHandle))
67 m_scene.Add(scene); 124 m_scenes[scene.RegionInfo.RegionHandle] = scene;
68 } 125 }
69 scene.EventManager.OnNewClient += OnNewClient; 126 scene.EventManager.OnNewClient += OnNewClient;
70 scene.EventManager.OnGridInstantMessage += OnGridInstantMessage; 127 scene.EventManager.OnGridInstantMessage += OnGridInstantMessage;
71 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; 128 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
72 scene.EventManager.OnMakeChildAgent += MakeChildAgent; 129 scene.EventManager.OnMakeChildAgent += MakeChildAgent;
73 scene.EventManager.OnClientClosed += ClientLoggedOut; 130 scene.EventManager.OnClientClosed += ClientClosed;
74 } 131 }
75 132
76 public void PostInitialise() 133 public void PostInitialise()
@@ -93,82 +150,104 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
93 150
94 #endregion 151 #endregion
95 152
96 public XmlRpcResponse processPresenceUpdate(XmlRpcRequest req) 153 public XmlRpcResponse processPresenceUpdateBulk(XmlRpcRequest req)
97 { 154 {
98 //m_log.Info("[FRIENDS]: Got Notification about a user! OMG");
99 Hashtable requestData = (Hashtable)req.Params[0]; 155 Hashtable requestData = (Hashtable)req.Params[0];
100 156
101 if (requestData.ContainsKey("agent_id") && requestData.ContainsKey("notify_id") && requestData.ContainsKey("status")) 157 List<UUID> friendsNotHere = new List<UUID>();
102 {
103 UUID notifyAgentId = UUID.Zero;
104 UUID notifyAboutAgentId = UUID.Zero;
105 bool notifyOnlineStatus = false;
106
107 if ((string)requestData["status"] == "TRUE")
108 notifyOnlineStatus = true;
109 158
110 UUID.TryParse((string)requestData["notify_id"], out notifyAgentId); 159 // this is called with the expectation that all the friends in the request are on this region-server.
111 160 // But as some time passed since we checked (on the other region-server, via the MessagingServer),
112 UUID.TryParse((string)requestData["agent_id"], out notifyAboutAgentId); 161 // some of the friends might have teleported away.
113 m_log.InfoFormat("[PRESENCE]: Got presence update for {0}, and we're telling {1}, with a status {2}", notifyAboutAgentId.ToString(), notifyAgentId.ToString(), notifyOnlineStatus.ToString()); 162 // Actually, even now, between this line and the sending below, some people could TP away. So,
114 ScenePresence avatar = GetRootPresenceFromAgentID(notifyAgentId); 163 // we'll have to lock the m_rootAgents list for the duration to prevent/delay that.
115 if (avatar != null) 164 lock(m_rootAgents)
165 {
166 List<ScenePresence> friendsHere = new List<ScenePresence>();
167 try
116 { 168 {
117 if (avatar.IsChildAgent) 169 UUID agentID = new UUID((string)requestData["agentID"]);
170 bool agentOnline = (bool)requestData["agentOnline"];
171 int count = (int)requestData["friendCount"];
172 for (int i = 0; i < count; ++i)
118 { 173 {
119 StoredFriendListUpdate sob = new StoredFriendListUpdate(); 174 UUID uuid;
120 sob.OnlineYN = notifyOnlineStatus; 175 if (UUID.TryParse((string)requestData["friendID_" + i], out uuid))
121 sob.storedAbout = notifyAboutAgentId;
122 sob.storedFor = notifyAgentId;
123 lock (StoredFriendListUpdates)
124 { 176 {
125 if (StoredFriendListUpdates.ContainsKey(notifyAgentId)) 177 if (m_rootAgents.ContainsKey(uuid)) friendsHere.Add(GetRootPresenceFromAgentID(uuid));
126 { 178 else friendsNotHere.Add(uuid);
127 StoredFriendListUpdates[notifyAgentId].Add(sob);
128 }
129 else
130 {
131 List<StoredFriendListUpdate> newitem = new List<StoredFriendListUpdate>();
132 newitem.Add(sob);
133 StoredFriendListUpdates.Add(notifyAgentId, newitem);
134 }
135 } 179 }
136 } 180 }
137 else 181
138 { 182 // now send, as long as they are still here...
139 if (notifyOnlineStatus) 183 UUID[] agentUUID = new UUID[] { agentID };
140 doFriendListUpdateOnline(notifyAboutAgentId); 184 if (agentOnline)
141 else
142 ClientLoggedOut(notifyAboutAgentId);
143 }
144 }
145 else
146 {
147 StoredFriendListUpdate sob = new StoredFriendListUpdate();
148 sob.OnlineYN = notifyOnlineStatus;
149 sob.storedAbout = notifyAboutAgentId;
150 sob.storedFor = notifyAgentId;
151 lock (StoredFriendListUpdates)
152 { 185 {
153 if (StoredFriendListUpdates.ContainsKey(notifyAgentId)) 186 foreach (ScenePresence agent in friendsHere)
154 { 187 {
155 StoredFriendListUpdates[notifyAgentId].Add(sob); 188 agent.ControllingClient.SendAgentOnline(agentUUID);
156 } 189 }
157 else 190 }
191 else
192 {
193 foreach (ScenePresence agent in friendsHere)
158 { 194 {
159 List<StoredFriendListUpdate> newitem = new List<StoredFriendListUpdate>(); 195 agent.ControllingClient.SendAgentOffline(agentUUID);
160 newitem.Add(sob);
161 StoredFriendListUpdates.Add(notifyAgentId, newitem);
162 } 196 }
163 } 197 }
164 } 198 }
199 catch(Exception e)
200 {
201 m_log.Warn("[FRIENDS]: Got exception while parsing presence_update_bulk request:", e);
202 }
203 }
165 204
205 // no need to lock anymore; if TPs happen now, worst case is that we have an additional agent in this region,
206 // which should be caught on the next iteration...
207 Hashtable result = new Hashtable();
208 int idx = 0;
209 foreach (UUID uuid in friendsNotHere)
210 {
211 result["friendID_" + idx++] = uuid.ToString();
166 } 212 }
167 else 213 result["friendCount"] = idx;
214
215 XmlRpcResponse response = new XmlRpcResponse();
216 response.Value = result;
217
218 return response;
219 }
220
221 public XmlRpcResponse processTerminateFriend(XmlRpcRequest req)
222 {
223 Hashtable requestData = (Hashtable)req.Params[0];
224
225 bool success = false;
226
227 UUID agentID;
228 UUID friendID;
229 if (requestData.ContainsKey("agentID") && UUID.TryParse((string)requestData["agentID"], out agentID) &&
230 requestData.ContainsKey("friendID") && UUID.TryParse((string)requestData["friendID"], out friendID))
168 { 231 {
169 m_log.Warn("[PRESENCE]: Malformed XMLRPC Presence request"); 232 // try to find it and if it is there, prevent it to vanish before we sent the message
233 lock(m_rootAgents)
234 {
235 if (m_rootAgents.ContainsKey(agentID))
236 {
237 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", friendID, agentID);
238 GetRootPresenceFromAgentID(agentID).ControllingClient.SendTerminateFriend(friendID);
239 success = true;
240 }
241 }
170 } 242 }
171 return new XmlRpcResponse(); 243
244 // return whether we were successful
245 Hashtable result = new Hashtable();
246 result["success"] = success;
247
248 XmlRpcResponse response = new XmlRpcResponse();
249 response.Value = result;
250 return response;
172 } 251 }
173 252
174 private void OnNewClient(IClientAPI client) 253 private void OnNewClient(IClientAPI client)
@@ -176,245 +255,52 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
176 // All friends establishment protocol goes over instant message 255 // All friends establishment protocol goes over instant message
177 // There's no way to send a message from the sim 256 // There's no way to send a message from the sim
178 // to a user to 'add a friend' without causing dialog box spam 257 // to a user to 'add a friend' without causing dialog box spam
179 //
180 // The base set of friends are added when the user signs on in their XMLRPC response
181 // Generated by LoginService. The friends are retreived from the database by the UserManager
182 258
183 // Subscribe to instant messages 259 // Subscribe to instant messages
184
185 client.OnInstantMessage += OnInstantMessage; 260 client.OnInstantMessage += OnInstantMessage;
186 client.OnApproveFriendRequest += OnApprovedFriendRequest; 261
262 // Friend list management
263 client.OnApproveFriendRequest += OnApproveFriendRequest;
187 client.OnDenyFriendRequest += OnDenyFriendRequest; 264 client.OnDenyFriendRequest += OnDenyFriendRequest;
188 client.OnTerminateFriendship += OnTerminateFriendship; 265 client.OnTerminateFriendship += OnTerminateFriendship;
266
267 // ... calling card handling...
189 client.OnOfferCallingCard += OnOfferCallingCard; 268 client.OnOfferCallingCard += OnOfferCallingCard;
190 client.OnAcceptCallingCard += OnAcceptCallingCard; 269 client.OnAcceptCallingCard += OnAcceptCallingCard;
191 client.OnDeclineCallingCard += OnDeclineCallingCard; 270 client.OnDeclineCallingCard += OnDeclineCallingCard;
192 271
193 doFriendListUpdateOnline(client.AgentId); 272 // we need this one exactly once per agent session (see comments in the handler below)
194 273 client.OnEconomyDataRequest += OnEconomyDataRequest;
195 }
196
197 private void doFriendListUpdateOnline(UUID AgentId)
198 {
199 List<FriendListItem> fl = new List<FriendListItem>();
200
201 //bool addFLback = false;
202
203 lock (FriendLists)
204 {
205 if (FriendLists.ContainsKey(AgentId))
206 {
207 fl = FriendLists[AgentId];
208 }
209 else
210 {
211 fl = m_scene[0].GetFriendList(AgentId);
212
213 //lock (FriendLists)
214 //{
215 if (!FriendLists.ContainsKey(AgentId))
216 FriendLists.Add(AgentId, fl);
217 //}
218 }
219 }
220
221 List<UUID> UpdateUsers = new List<UUID>();
222
223 foreach (FriendListItem f in fl)
224 {
225 if (m_rootAgents.ContainsKey(f.Friend))
226 {
227 if (f.onlinestatus == false)
228 {
229 UpdateUsers.Add(f.Friend);
230 f.onlinestatus = true;
231 }
232 }
233 }
234 foreach (UUID user in UpdateUsers)
235 {
236 ScenePresence av = GetRootPresenceFromAgentID(user);
237 if (av != null)
238 {
239 List<FriendListItem> usrfl = new List<FriendListItem>();
240
241 lock (FriendLists)
242 {
243 usrfl = FriendLists[user];
244 }
245
246 lock (usrfl)
247 {
248 foreach (FriendListItem fli in usrfl)
249 {
250 if (fli.Friend == AgentId)
251 {
252 fli.onlinestatus = true;
253 UUID[] Agents = new UUID[1];
254 Agents[0] = AgentId;
255 av.ControllingClient.SendAgentOnline(Agents);
256
257 }
258 }
259 }
260 }
261 }
262
263 if (UpdateUsers.Count > 0)
264 {
265 ScenePresence avatar = GetRootPresenceFromAgentID(AgentId);
266 if (avatar != null)
267 {
268 avatar.ControllingClient.SendAgentOnline(UpdateUsers.ToArray());
269 }
270 274
271 } 275 // if it leaves, we want to know, too
276 client.OnLogout += OnLogout;
272 } 277 }
273 278
274 private void ClientLoggedOut(UUID AgentId) 279 private void ClientClosed(UUID AgentId)
275 { 280 {
281 // agent's client was closed. As we handle logout in OnLogout, this here has only to handle
282 // TPing away (root agent is closed) or TPing/crossing in a region far enough away (client
283 // agent is closed).
284 // NOTE: In general, this doesn't mean that the agent logged out, just that it isn't around
285 // in one of the regions here anymore.
276 lock (m_rootAgents) 286 lock (m_rootAgents)
277 { 287 {
278 if (m_rootAgents.ContainsKey(AgentId)) 288 if (m_rootAgents.ContainsKey(AgentId))
279 { 289 {
280 m_rootAgents.Remove(AgentId); 290 m_rootAgents.Remove(AgentId);
281 m_log.Info("[FRIEND]: Removing " + AgentId + ". Agent logged out."); 291 m_log.Info("[FRIEND]: Removing " + AgentId + ". Agent was closed.");
282 }
283 }
284 List<FriendListItem> lfli = new List<FriendListItem>();
285 lock (FriendLists)
286 {
287 if (FriendLists.ContainsKey(AgentId))
288 {
289 lfli = FriendLists[AgentId];
290 }
291 }
292 List<UUID> updateUsers = new List<UUID>();
293 foreach (FriendListItem fli in lfli)
294 {
295 if (fli.onlinestatus == true)
296 {
297 updateUsers.Add(fli.Friend);
298 }
299 }
300 lock (updateUsers)
301 {
302 for (int i = 0; i < updateUsers.Count; i++)
303 {
304 List<FriendListItem> flfli = new List<FriendListItem>();
305 try
306 {
307 lock (FriendLists)
308 {
309 if (FriendLists.ContainsKey(updateUsers[i]))
310 flfli = FriendLists[updateUsers[i]];
311 }
312 }
313 catch (IndexOutOfRangeException)
314 {
315 // Ignore the index out of range exception.
316 // This causes friend lists to get out of sync slightly.. however
317 // prevents a sim crash.
318 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
319 }
320 catch (ArgumentOutOfRangeException)
321 {
322 // Ignore the index out of range exception.
323 // This causes friend lists to get out of sync slightly.. however
324 // prevents a sim crash.
325 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
326 }
327
328 for (int j = 0; j < flfli.Count; j++)
329 {
330 try
331 {
332 if (flfli[i].Friend == AgentId)
333 {
334 flfli[i].onlinestatus = false;
335 }
336 }
337
338 catch (IndexOutOfRangeException)
339 {
340 // Ignore the index out of range exception.
341 // This causes friend lists to get out of sync slightly.. however
342 // prevents a sim crash.
343 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
344 }
345 catch (ArgumentOutOfRangeException)
346 {
347 // Ignore the index out of range exception.
348 // This causes friend lists to get out of sync slightly.. however
349 // prevents a sim crash.
350 m_log.Info("[FRIEND]: Unable to enumerate last friendlist user. User logged off");
351 }
352 }
353 }
354
355 for (int i = 0; i < updateUsers.Count; i++)
356 {
357 ScenePresence av = GetRootPresenceFromAgentID(updateUsers[i]);
358 if (av != null)
359 {
360 UUID[] agents = new UUID[1];
361 agents[0] = AgentId;
362 av.ControllingClient.SendAgentOffline(agents);
363 }
364 } 292 }
365 } 293 }
366 lock (FriendLists)
367 {
368 FriendLists.Remove(AgentId);
369 }
370 } 294 }
371 295
372 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) 296 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID)
373 { 297 {
374 lock (m_rootAgents) 298 lock (m_rootAgents)
375 { 299 {
376 if (m_rootAgents.ContainsKey(avatar.UUID)) 300 m_rootAgents[avatar.UUID] = avatar.RegionHandle;
377 { 301 m_log.Info("[FRIEND]: Claiming " + avatar.Firstname + " " + avatar.Lastname + " in region:" + avatar.RegionHandle + ".");
378 if (avatar.RegionHandle != m_rootAgents[avatar.UUID]) 302 // Claim User! my user! Mine mine mine!
379 {
380 m_rootAgents[avatar.UUID] = avatar.RegionHandle;
381 m_log.Info("[FRIEND]: Claiming " + avatar.Firstname + " " + avatar.Lastname + " in region:" + avatar.RegionHandle + ".");
382 if (avatar.JID.Length > 0)
383 {
384 // JId avatarID = new JId(avatar.JID);
385 // REST Post XMPP Stanzas!
386 }
387 // Claim User! my user! Mine mine mine!
388 }
389 }
390 else
391 {
392 m_rootAgents.Add(avatar.UUID, avatar.RegionHandle);
393 m_log.Info("[FRIEND]: Claiming " + avatar.Firstname + " " + avatar.Lastname + " in region:" + avatar.RegionHandle + ".");
394
395 List<StoredFriendListUpdate> updateme = new List<StoredFriendListUpdate>();
396 lock (StoredFriendListUpdates)
397 {
398 if (StoredFriendListUpdates.ContainsKey(avatar.UUID))
399 {
400 updateme = StoredFriendListUpdates[avatar.UUID];
401 StoredFriendListUpdates.Remove(avatar.UUID);
402 }
403 }
404
405 if (updateme.Count > 0)
406 {
407 foreach (StoredFriendListUpdate u in updateme)
408 {
409 if (u.OnlineYN)
410 doFriendListUpdateOnline(u.storedAbout);
411 else
412 ClientLoggedOut(u.storedAbout);
413 }
414 }
415 }
416 } 303 }
417 //m_log.Info("[FRIEND]: " + avatar.Name + " status:" + (!avatar.IsChildAgent).ToString());
418 } 304 }
419 305
420 private void MakeChildAgent(ScenePresence avatar) 306 private void MakeChildAgent(ScenePresence avatar)
@@ -423,6 +309,8 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
423 { 309 {
424 if (m_rootAgents.ContainsKey(avatar.UUID)) 310 if (m_rootAgents.ContainsKey(avatar.UUID))
425 { 311 {
312 // only delete if the region matches. As this is a shared module, the avatar could be
313 // root agent in another region on this server.
426 if (m_rootAgents[avatar.UUID] == avatar.RegionHandle) 314 if (m_rootAgents[avatar.UUID] == avatar.RegionHandle)
427 { 315 {
428 m_rootAgents.Remove(avatar.UUID); 316 m_rootAgents.Remove(avatar.UUID);
@@ -435,12 +323,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
435 private ScenePresence GetRootPresenceFromAgentID(UUID AgentID) 323 private ScenePresence GetRootPresenceFromAgentID(UUID AgentID)
436 { 324 {
437 ScenePresence returnAgent = null; 325 ScenePresence returnAgent = null;
438 lock (m_scene) 326 lock (m_scenes)
439 { 327 {
440 ScenePresence queryagent = null; 328 ScenePresence queryagent = null;
441 for (int i = 0; i < m_scene.Count; i++) 329 foreach (Scene scene in m_scenes.Values)
442 { 330 {
443 queryagent = m_scene[i].GetScenePresence(AgentID); 331 queryagent = scene.GetScenePresence(AgentID);
444 if (queryagent != null) 332 if (queryagent != null)
445 { 333 {
446 if (!queryagent.IsChildAgent) 334 if (!queryagent.IsChildAgent)
@@ -457,12 +345,12 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
457 private ScenePresence GetAnyPresenceFromAgentID(UUID AgentID) 345 private ScenePresence GetAnyPresenceFromAgentID(UUID AgentID)
458 { 346 {
459 ScenePresence returnAgent = null; 347 ScenePresence returnAgent = null;
460 lock (m_scene) 348 lock (m_scenes)
461 { 349 {
462 ScenePresence queryagent = null; 350 ScenePresence queryagent = null;
463 for (int i = 0; i < m_scene.Count; i++) 351 foreach (Scene scene in m_scenes.Values)
464 { 352 {
465 queryagent = m_scene[i].GetScenePresence(AgentID); 353 queryagent = scene.GetScenePresence(AgentID);
466 if (queryagent != null) 354 if (queryagent != null)
467 { 355 {
468 returnAgent = queryagent; 356 returnAgent = queryagent;
@@ -485,141 +373,160 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
485 // Friend Requests go by Instant Message.. using the dialog param 373 // Friend Requests go by Instant Message.. using the dialog param
486 // https://wiki.secondlife.com/wiki/ImprovedInstantMessage 374 // https://wiki.secondlife.com/wiki/ImprovedInstantMessage
487 375
488 // 38 == Offer friendship 376 if (dialog == (byte)InstantMessageDialog.FriendshipOffered) // 38
489 if (dialog == (byte) 38)
490 { 377 {
491 UUID friendTransactionID = UUID.Random(); 378 // this is triggered by the initiating agent and has two parts:
379 // A local agent offers friendship to some possibly remote friend.
380 // A IM is triggered, processed here (1), sent to the destination region,
381 // and processed there in this part of the code again (2).
382 // (1) has fromAgentSession != UUID.Zero,
383 // (2) has fromAgentSession == UUID.Zero (don't leak agent sessions to other agents)
384 // For (1), build the IM to send to the other region (and trigger sending it)
385 // FOr (2), just store the transaction; we will wait for Approval or Decline
386
387 // some properties are misused here:
388 // fromAgentName is the *destination* name (the friend we offer friendship to)
389
390 if (fromAgentSession != UUID.Zero)
391 {
392 // (1)
393 // send the friendship-offer to the target
394 m_log.InfoFormat("[FRIEND]: Offer(38) - From: {0}, FromName: {1} To: {2}, Session: {3}, Message: {4}, Offline {5}",
395 fromAgentID, fromAgentName, toAgentID, imSessionID, message, offline);
396
397 UUID transactionID = UUID.Random();
398
399 GridInstantMessage msg = new GridInstantMessage();
400 msg.fromAgentID = fromAgentID.Guid;
401 msg.fromAgentSession = UUID.Zero.Guid; // server IMs don't have a session
402 msg.toAgentID = toAgentID.Guid;
403 msg.imSessionID = transactionID.Guid; // Start new transaction
404 m_log.DebugFormat("[FRIEND]: new transactionID: {0}", msg.imSessionID);
405 msg.timestamp = timestamp;
406 if (client != null)
407 {
408 msg.fromAgentName = client.Name; // fromAgentName;
409 }
410 else
411 {
412 msg.fromAgentName = "(hippos)"; // Added for posterity. This means that we can't figure out who sent it
413 }
414 msg.message = message;
415 msg.dialog = dialog;
416 msg.fromGroup = fromGroup;
417 msg.offline = offline;
418 msg.ParentEstateID = ParentEstateID;
419 msg.Position = Position;
420 msg.RegionID = RegionID.Guid;
421 msg.binaryBucket = binaryBucket;
422
423 m_log.DebugFormat("[FRIEND]: storing transactionID {0} on sender side", transactionID);
424 lock (m_pendingFriendRequests)
425 {
426 m_pendingFriendRequests.Add(transactionID, new Transaction(fromAgentID, fromAgentName));
427 outPending();
428 }
492 429
493 m_pendingFriendRequests.Add(friendTransactionID, fromAgentID); 430 // we don't want to get that new IM into here if we aren't local, as only on the destination
431 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
432 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
433 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
434 if(GetAnyPresenceFromAgentID(toAgentID) != null) recv |= InstantMessageReceiver.FriendsModule;
494 435
495 m_log.Info("[FRIEND]: 38 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + 436 // We don't really care which local scene we pipe it through.
496 message); 437 m_initialScene.TriggerGridInstantMessage(msg, recv);
497 GridInstantMessage msg = new GridInstantMessage();
498 msg.fromAgentID = fromAgentID.Guid;
499 msg.fromAgentSession = fromAgentSession.Guid;
500 msg.toAgentID = toAgentID.Guid;
501 msg.imSessionID = friendTransactionID.Guid; // This is the item we're mucking with here
502 m_log.Info("[FRIEND]: Filling Session: " + msg.imSessionID.ToString());
503 msg.timestamp = timestamp;
504 if (client != null)
505 {
506 msg.fromAgentName = client.Name; // fromAgentName;
507 } 438 }
508 else 439 else
509 { 440 {
510 msg.fromAgentName = "(hippos)"; // Added for posterity. This means that we can't figure out who sent it 441 // (2)
442 // we are on the receiving end here; just add the transactionID to the stored transactions for later lookup
443 m_log.DebugFormat("[FRIEND]: storing transactionID {0} on receiver side", imSessionID);
444 lock (m_pendingFriendRequests)
445 {
446 // if both are on the same region-server, the transaction is stored already, but we have to update the name
447 if (m_pendingFriendRequests.ContainsKey(imSessionID))
448 {
449 m_pendingFriendRequests[imSessionID].agentName = fromAgentName;
450 m_pendingFriendRequests[imSessionID].count++;
451 }
452 else m_pendingFriendRequests.Add(imSessionID, new Transaction(fromAgentID, fromAgentName));
453 outPending();
454 }
511 } 455 }
512 msg.message = message;
513 msg.dialog = dialog;
514 msg.fromGroup = fromGroup;
515 msg.offline = offline;
516 msg.ParentEstateID = ParentEstateID;
517 msg.Position = Position;
518 msg.RegionID = RegionID.Guid;
519 msg.binaryBucket = binaryBucket;
520 // We don't really care which scene we pipe it through.
521 m_scene[0].TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
522 } 456 }
523 457 else if (dialog == (byte)InstantMessageDialog.FriendshipAccepted) // 39
524 // 39 == Accept Friendship
525 if (dialog == (byte) 39)
526 { 458 {
527 m_log.Info("[FRIEND]: 39 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + 459 // accepting the friendship offer causes a type 39 IM being sent to the (possibly remote) initiator
528 message); 460 // toAgentID is initiator, fromAgentID is new friend (which just approved)
529 } 461 m_log.DebugFormat("[FRIEND]: 39 - from client {0}, agentSession {1}, agent {2} {3}, imsession {4} to {5}: {6} (dialog {7})",
462 client != null ? client.AgentId.ToString() : "<null>", fromAgentSession,
463 fromAgentID, fromAgentName, imSessionID, toAgentID, message, dialog);
464 lock (m_pendingFriendRequests)
465 {
466 if (!m_pendingFriendRequests.ContainsKey(imSessionID))
467 {
468 m_log.DebugFormat("[FRIEND]: Got friendship approval from {0} to {1} without matching transaction {2}",
469 fromAgentID, toAgentID, imSessionID);
470 return; // unknown transaction
471 }
472 // else found pending friend request with that transaction => remove it if we handled all
473 if (--m_pendingFriendRequests[imSessionID].count <= 0) m_pendingFriendRequests.Remove(imSessionID);
474 outPending();
475 }
530 476
531 // 40 == Decline Friendship 477 // a new friend was added in the initiator's and friend's data, so the cache entries are wrong now.
532 if (dialog == (byte) 40) 478 lock (m_friendLists)
533 { 479 {
534 m_log.Info("[FRIEND]: 40 - From:" + fromAgentID.ToString() + " To: " + toAgentID.ToString() + " Session:" + imSessionID.ToString() + " Message:" + 480 m_friendLists.Invalidate(toAgentID);
535 message); 481 m_friendLists.Invalidate(fromAgentID);
536 } 482 }
537 }
538 483
539 private void OnApprovedFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders) 484 // now send presence update and add a calling card for the new friend
540 {
541 if (m_pendingFriendRequests.ContainsKey(transactionID))
542 {
543 // Found Pending Friend Request with that Transaction..
544 Scene SceneAgentIn = m_scene[0];
545 485
546 // Found Pending Friend Request with that Transaction.. 486 ScenePresence initiator = GetAnyPresenceFromAgentID(toAgentID);
547 ScenePresence agentpresence = GetRootPresenceFromAgentID(agentID); 487 if (initiator == null)
548 if (agentpresence != null)
549 { 488 {
550 SceneAgentIn = agentpresence.Scene; 489 // quite wrong. Shouldn't happen.
490 m_log.WarnFormat("[FRIEND]: Coudn't find initiator of friend request {0}", toAgentID);
491 return;
551 } 492 }
552 493
553 // Compose response to other agent. 494 // tell initiator that friend is online
554 GridInstantMessage msg = new GridInstantMessage(); 495 initiator.ControllingClient.SendAgentOnline(new UUID[] { fromAgentID });
555 msg.toAgentID = m_pendingFriendRequests[transactionID].Guid;
556 msg.fromAgentID = agentID.Guid;
557 msg.fromAgentName = client.Name;
558 msg.fromAgentSession = client.SessionId.Guid;
559 msg.fromGroup = false;
560 msg.imSessionID = transactionID.Guid;
561 msg.message = agentID.Guid.ToString();
562 msg.ParentEstateID = 0;
563 msg.timestamp = (uint) Util.UnixTimeSinceEpoch();
564 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.Guid;
565 msg.dialog = (byte) 39; // Approved friend request
566 msg.Position = Vector3.Zero;
567 msg.offline = (byte) 0;
568 msg.binaryBucket = new byte[0];
569 // We don't really care which scene we pipe it through, it goes to the shared IM Module and/or the database
570
571 SceneAgentIn.TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
572 SceneAgentIn.StoreAddFriendship(m_pendingFriendRequests[transactionID], agentID, (uint) 1);
573
574
575 //UUID[] Agents = new UUID[1];
576 //Agents[0] = msg.toAgentID;
577 //av.ControllingClient.SendAgentOnline(Agents);
578
579 m_pendingFriendRequests.Remove(transactionID);
580 // TODO: Inform agent that the friend is online
581 }
582 }
583 496
584 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders) 497 // find the folder for the friend...
585 { 498 InventoryFolderImpl folder =
586 if (m_pendingFriendRequests.ContainsKey(transactionID)) 499 initiator.Scene.CommsManager.UserProfileCacheService.GetUserDetails(toAgentID).FindFolderForType((int)InventoryType.CallingCard);
500 if (folder != null)
501 {
502 // ... and add the calling card
503 CreateCallingCard(initiator.ControllingClient, fromAgentID, folder.ID, fromAgentName);
504 }
505 }
506 else if (dialog == (byte)InstantMessageDialog.FriendshipDeclined) // 40
587 { 507 {
588 Scene SceneAgentIn = m_scene[0]; 508 // declining the friendship offer causes a type 40 IM being sent to the (possibly remote) initiator
589 509 // toAgentID is initiator, fromAgentID declined friendship
590 // Found Pending Friend Request with that Transaction.. 510 m_log.DebugFormat("[FRIEND]: 40 - from client {0}, agentSession {1}, agent {2} {3}, imsession {4} to {5}: {6} (dialog {7})",
591 ScenePresence agentpresence = GetRootPresenceFromAgentID(agentID); 511 client != null ? client.AgentId.ToString() : "<null>", fromAgentSession,
592 if (agentpresence != null) 512 fromAgentID, fromAgentName, imSessionID, toAgentID, message, dialog);
513
514 // not much to do, just clean up the transaction...
515 lock (m_pendingFriendRequests)
593 { 516 {
594 SceneAgentIn = agentpresence.Scene; 517 if (!m_pendingFriendRequests.ContainsKey(imSessionID))
518 {
519 m_log.DebugFormat("[FRIEND]: Got friendship denial from {0} to {1} without matching transaction {2}",
520 fromAgentID, toAgentID, imSessionID);
521 return; // unknown transaction
522 }
523 // else found pending friend request with that transaction => remove it if we handled all
524 if (--m_pendingFriendRequests[imSessionID].count <= 0) m_pendingFriendRequests.Remove(imSessionID);
525 outPending();
595 } 526 }
596 // Compose response to other agent.
597 GridInstantMessage msg = new GridInstantMessage();
598 msg.toAgentID = m_pendingFriendRequests[transactionID].Guid;
599 msg.fromAgentID = agentID.Guid;
600 msg.fromAgentName = client.Name;
601 msg.fromAgentSession = client.SessionId.Guid;
602 msg.fromGroup = false;
603 msg.imSessionID = transactionID.Guid;
604 msg.message = agentID.Guid.ToString();
605 msg.ParentEstateID = 0;
606 msg.timestamp = (uint) Util.UnixTimeSinceEpoch();
607 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.Guid;
608 msg.dialog = (byte) 40; // Deny friend request
609 msg.Position = Vector3.Zero;
610 msg.offline = (byte) 0;
611 msg.binaryBucket = new byte[0];
612 SceneAgentIn.TriggerGridInstantMessage(msg, InstantMessageReceiver.IMModule);
613 m_pendingFriendRequests.Remove(transactionID);
614 } 527 }
615 } 528 }
616 529
617 private void OnTerminateFriendship(IClientAPI client, UUID agent, UUID exfriendID)
618 {
619 m_scene[0].StoreRemoveFriendship(agent, exfriendID);
620 // TODO: Inform the client that the ExFriend is offline
621 }
622
623 private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule) 530 private void OnGridInstantMessage(GridInstantMessage msg, InstantMessageReceiver whichModule)
624 { 531 {
625 if ((whichModule & InstantMessageReceiver.FriendsModule) == 0) 532 if ((whichModule & InstantMessageReceiver.FriendsModule) == 0)
@@ -633,6 +540,182 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
633 msg.binaryBucket); 540 msg.binaryBucket);
634 } 541 }
635 542
543 private void OnApproveFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders)
544 {
545 m_log.DebugFormat("[FRIEND]: Got approve friendship from {0} {1}, agentID {2}, tid {3}",
546 client.Name, client.AgentId, agentID, transactionID);
547 Transaction transaction;
548 lock (m_pendingFriendRequests)
549 {
550 if (!m_pendingFriendRequests.TryGetValue(transactionID, out transaction))
551 {
552 m_log.DebugFormat("[FRIEND]: Got friendship approval {0} from {1} ({2}) without matching transaction {3}",
553 agentID, client.AgentId, client.Name, transactionID);
554 return; // unknown transaction
555 }
556 // else found pending friend request with that transaction => remove if done with all
557 if(--m_pendingFriendRequests[transactionID].count <= 0) m_pendingFriendRequests.Remove(transactionID);
558 outPending();
559 }
560
561 UUID friendID = transaction.agentID;
562 m_log.DebugFormat("[FRIEND]: {0} ({1}) approved friendship request from {2}",
563 client.Name, client.AgentId, friendID);
564
565 Scene SceneAgentIn = m_initialScene;
566 // we need any presence to send the packets to, not necessarily the root agent...
567 ScenePresence agentpresence = GetAnyPresenceFromAgentID(agentID);
568 if (agentpresence != null)
569 {
570 SceneAgentIn = agentpresence.Scene;
571 }
572
573 // store the new friend persistently for both avatars
574 SceneAgentIn.StoreAddFriendship(friendID, agentID, (uint) FriendRights.CanSeeOnline);
575
576 // The cache entries aren't valid anymore either, as we just added a friend to both sides.
577 lock (m_friendLists)
578 {
579 m_friendLists.Invalidate(agentID);
580 m_friendLists.Invalidate(friendID);
581 }
582
583 // create calling card
584 CreateCallingCard(client, friendID, callingCardFolders[0], transaction.agentName);
585
586 // Compose response to other agent.
587 GridInstantMessage msg = new GridInstantMessage();
588 msg.toAgentID = friendID.Guid;
589 msg.fromAgentID = agentID.Guid;
590 msg.fromAgentName = client.Name;
591 msg.fromAgentSession = UUID.Zero.Guid; // server IMs don't have a session
592 msg.fromGroup = false;
593 msg.imSessionID = transactionID.Guid;
594 msg.message = agentID.Guid.ToString();
595 msg.ParentEstateID = 0;
596 msg.timestamp = (uint) Util.UnixTimeSinceEpoch();
597 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.Guid;
598 msg.dialog = (byte) InstantMessageDialog.FriendshipAccepted;
599 msg.Position = Vector3.Zero;
600 msg.offline = (byte) 0;
601 msg.binaryBucket = new byte[0];
602
603 // we don't want to get that new IM into here if we aren't local, as only on the destination
604 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
605 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
606 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
607 if(GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule;
608
609 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler
610 // of the type 39 IM
611 SceneAgentIn.TriggerGridInstantMessage(msg, recv);
612
613 // tell client that new friend is online
614 client.SendAgentOnline(new UUID[] { friendID });
615 }
616
617 private void OnDenyFriendRequest(IClientAPI client, UUID agentID, UUID transactionID, List<UUID> callingCardFolders)
618 {
619 m_log.DebugFormat("[FRIEND]: Got deny friendship from {0} {1}, agentID {2}, tid {3}",
620 client.Name, client.AgentId, agentID, transactionID);
621 Transaction transaction;
622 lock (m_pendingFriendRequests)
623 {
624 if(!m_pendingFriendRequests.TryGetValue(transactionID, out transaction))
625 {
626 m_log.DebugFormat("[FRIEND]: Got friendship denial {0} from {1} ({2}) without matching transaction {3}",
627 agentID, client.AgentId, client.Name, transactionID);
628 return;
629 }
630 // else found pending friend request with that transaction.
631 if(--m_pendingFriendRequests[transactionID].count <= 0) m_pendingFriendRequests.Remove(transactionID);
632 outPending();
633 }
634 UUID friendID = transaction.agentID;
635
636 Scene SceneAgentIn = m_initialScene;
637 ScenePresence agentpresence = GetRootPresenceFromAgentID(agentID);
638 if (agentpresence != null)
639 {
640 SceneAgentIn = agentpresence.Scene;
641 }
642
643 // Compose response to other agent.
644 GridInstantMessage msg = new GridInstantMessage();
645 msg.toAgentID = friendID.Guid;
646 msg.fromAgentID = agentID.Guid;
647 msg.fromAgentName = client.Name;
648 msg.fromAgentSession = UUID.Zero.Guid; // server IMs don't have a session
649 msg.fromGroup = false;
650 msg.imSessionID = transactionID.Guid;
651 msg.message = agentID.Guid.ToString();
652 msg.ParentEstateID = 0;
653 msg.timestamp = (uint) Util.UnixTimeSinceEpoch();
654 msg.RegionID = SceneAgentIn.RegionInfo.RegionID.Guid;
655 msg.dialog = (byte) InstantMessageDialog.FriendshipDeclined;
656 msg.Position = Vector3.Zero;
657 msg.offline = (byte) 0;
658 msg.binaryBucket = new byte[0];
659
660 // we don't want to get that new IM into here if we aren't local, as only on the destination
661 // should receive it. If we *are* local, *we* are the destination, so we have to receive it.
662 // As grid-IMs are routed to all modules (in contrast to local IMs), we have to decide here.
663 InstantMessageReceiver recv = InstantMessageReceiver.IMModule;
664 if(GetAnyPresenceFromAgentID(friendID) != null) recv |= InstantMessageReceiver.FriendsModule;
665
666 // now we have to inform the agent about the friend. For the opposite direction, this happens in the handler
667 // of the type 39 IM
668 SceneAgentIn.TriggerGridInstantMessage(msg, recv);
669 }
670
671 private void OnTerminateFriendship(IClientAPI client, UUID agentID, UUID exfriendID)
672 {
673 // client.AgentId == agentID!
674
675 // this removes the friends from the stored friendlists. After the next login, they will be gone...
676 m_initialScene.StoreRemoveFriendship(agentID, exfriendID);
677
678 // ... now tell the two involved clients that they aren't friends anymore.
679
680 // I don't know why we have to tell <agent>, as this was caused by her, but that's how it works in SL...
681 client.SendTerminateFriend(exfriendID);
682
683 // now send the friend, if online
684 ScenePresence presence = GetAnyPresenceFromAgentID(exfriendID);
685 if (presence != null)
686 {
687 m_log.DebugFormat("[FRIEND]: Sending terminate friend {0} to agent {1}", agentID, exfriendID);
688 presence.ControllingClient.SendTerminateFriend(agentID);
689 }
690 else
691 {
692 // retry 3 times, in case the agent TPed from the last known region...
693 for(int retry = 0; retry < 3; ++retry)
694 {
695 // wasn't sent, so ex-friend wasn't around on this region-server. Fetch info and try to send
696 UserAgentData data = m_initialScene.CommsManager.UserService.GetAgentByUUID(exfriendID);
697 if (!data.AgentOnline)
698 {
699 m_log.DebugFormat("[FRIEND]: {0} is offline, so not sending TerminateFriend", exfriendID);
700 break; // if ex-friend isn't online, we don't need to send
701 }
702
703 m_log.DebugFormat("[FRIEND]: Sending remote terminate friend {0} to agent {1}@{2}",
704 agentID, exfriendID, data.Handle);
705
706 // try to send to foreign region, retry if it fails (friend TPed away, for example)
707 if (m_initialScene.TriggerTerminateFriend(data.Handle, exfriendID, agentID)) break;
708 }
709 }
710
711 // clean up cache: FriendList is wrong now...
712 lock (m_friendLists)
713 {
714 m_friendLists.Invalidate(agentID);
715 m_friendLists.Invalidate(exfriendID);
716 }
717 }
718
636 #endregion 719 #endregion
637 720
638 #region CallingCards 721 #region CallingCards
@@ -650,7 +733,10 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
650 return; 733 return;
651 } 734 }
652 735
653 m_pendingCallingcardRequests[transactionID] = client.AgentId; 736 lock (m_pendingCallingcardRequests)
737 {
738 m_pendingCallingcardRequests[transactionID] = client.AgentId;
739 }
654 // inform the destination agent about the offer 740 // inform the destination agent about the offer
655 destAgent.ControllingClient.SendOfferCallingCard(client.AgentId, transactionID); 741 destAgent.ControllingClient.SendOfferCallingCard(client.AgentId, transactionID);
656 } 742 }
@@ -687,19 +773,25 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
687 client.FirstName, client.LastName, 773 client.FirstName, client.LastName,
688 transactionID, folderID); 774 transactionID, folderID);
689 UUID destID; 775 UUID destID;
690 if (m_pendingCallingcardRequests.TryGetValue(transactionID, out destID)) 776 lock (m_pendingCallingcardRequests)
691 { 777 {
778 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID))
779 {
780 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
781 client.Name);
782 return;
783 }
784 // else found pending calling card request with that transaction.
692 m_pendingCallingcardRequests.Remove(transactionID); 785 m_pendingCallingcardRequests.Remove(transactionID);
786 }
693 787
694 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
695 // inform sender of the card that destination declined the offer
696 if (destAgent != null) destAgent.ControllingClient.SendAcceptCallingCard(transactionID);
697 788
698 // put a calling card into the inventory of receiver 789 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
699 CreateCallingCard(client, destID, folderID, destAgent.Name); 790 // inform sender of the card that destination declined the offer
700 } 791 if (destAgent != null) destAgent.ControllingClient.SendAcceptCallingCard(transactionID);
701 else m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} {1} without an offer before.", 792
702 client.FirstName, client.LastName); 793 // put a calling card into the inventory of receiver
794 CreateCallingCard(client, destID, folderID, destAgent.Name);
703 } 795 }
704 796
705 private void OnDeclineCallingCard(IClientAPI client, UUID transactionID) 797 private void OnDeclineCallingCard(IClientAPI client, UUID transactionID)
@@ -707,25 +799,230 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Friends
707 m_log.DebugFormat("[CALLING CARD]: User {0} declined card, tid {2}", 799 m_log.DebugFormat("[CALLING CARD]: User {0} declined card, tid {2}",
708 client.AgentId, transactionID); 800 client.AgentId, transactionID);
709 UUID destID; 801 UUID destID;
710 if (m_pendingCallingcardRequests.TryGetValue(transactionID, out destID)) 802 lock (m_pendingCallingcardRequests)
711 { 803 {
804 if (!m_pendingCallingcardRequests.TryGetValue(transactionID, out destID))
805 {
806 m_log.WarnFormat("[CALLING CARD]: Got a AcceptCallingCard from {0} without an offer before.",
807 client.Name);
808 return;
809 }
810 // else found pending calling card request with that transaction.
712 m_pendingCallingcardRequests.Remove(transactionID); 811 m_pendingCallingcardRequests.Remove(transactionID);
812 }
813
814 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID);
815 // inform sender of the card that destination declined the offer
816 if (destAgent != null) destAgent.ControllingClient.SendDeclineCallingCard(transactionID);
817 }
818
819 private void SendPresenceState(IClientAPI client, List<FriendListItem> friendList, bool iAmOnline)
820 {
821 m_log.DebugFormat("[FRIEND]: {0} logged {1}; sending presence updates", client.Name, iAmOnline ? "in" : "out");
822
823 if (friendList == null || friendList.Count == 0)
824 {
825 m_log.DebugFormat("[FRIEND]: {0} doesn't have friends.", client.Name);
826 return; // nothing we can do if she doesn't have friends...
827 }
828
829 // collect sets of friendIDs; to send to (online and offline), and to receive from
830 // TODO: If we ever switch to .NET >= 3, replace those Lists with HashSets.
831 // I can't believe that we have Dictionaries, but no Sets, considering Java introduced them years ago...
832 List<UUID> friendIDsToSendTo = new List<UUID>();
833 List<UUID> friendIDsToReceiveFromOffline = new List<UUID>();
834 List<UUID> friendIDsToReceiveFromOnline = new List<UUID>();
835 foreach (FriendListItem item in friendList)
836 {
837 if (((item.FriendListOwnerPerms | item.FriendPerms) & (uint)FriendRights.CanSeeOnline) != 0)
838 {
839 // friend is allowed to see my presence => add
840 if ((item.FriendListOwnerPerms & (uint)FriendRights.CanSeeOnline) != 0) friendIDsToSendTo.Add(item.Friend);
841
842 // I'm allowed to see friend's presence => add as offline, we might reconsider in a momnet...
843 if ((item.FriendPerms & (uint)FriendRights.CanSeeOnline) != 0) friendIDsToReceiveFromOffline.Add(item.Friend);
844 }
845 }
846
847
848
849 // we now have a list of "interesting" friends (which we have to find out on-/offline state for),
850 // friends we want to send our online state to (if *they* are online, too), and
851 // friends we want to receive online state for (currently unknown whether online or not)
852
853 // as this processing might take some time and friends might TP away, we try up to three times to
854 // reach them. Most of the time, we *will* reach them, and this loop won't loop
855 int retry = 0;
856 do
857 {
858 // build a list of friends to look up region-information and on-/offline-state for
859 List<UUID> friendIDsToLookup = new List<UUID>(friendIDsToSendTo);
860 foreach (UUID uuid in friendIDsToReceiveFromOffline)
861 {
862 if(!friendIDsToLookup.Contains(uuid)) friendIDsToLookup.Add(uuid);
863 }
864
865 m_log.DebugFormat("[FRIEND]: {0} to lookup, {1} to send to, {2} to receive from for agent {3}",
866 friendIDsToLookup.Count, friendIDsToSendTo.Count, friendIDsToReceiveFromOffline.Count, client.Name);
867
868 // we have to fetch FriendRegionInfos, as the (cached) FriendListItems don't
869 // necessarily contain the correct online state...
870 Dictionary<UUID, FriendRegionInfo> friendRegions = m_initialScene.GetFriendRegionInfos(friendIDsToLookup);
871 m_log.DebugFormat("[FRIEND]: Found {0} regionInfos for {1} friends of {2}",
872 friendRegions.Count, friendIDsToLookup.Count, client.Name);
873
874 // argument for SendAgentOn/Offline; we shouldn't generate that repeatedly within loops.
875 UUID[] agentArr = new UUID[] { client.AgentId };
876
877 // first, send to friend presence state to me, if I'm online...
878 if (iAmOnline)
879 {
880 for (int i = friendIDsToReceiveFromOffline.Count - 1; i >= 0; --i)
881 {
882 UUID uuid = friendIDsToReceiveFromOffline[i];
883 FriendRegionInfo info;
884 if (friendRegions.TryGetValue(uuid, out info) && info.isOnline)
885 {
886 friendIDsToReceiveFromOffline.RemoveAt(i);
887 friendIDsToReceiveFromOnline.Add(uuid);
888 }
889 }
890 m_log.DebugFormat("[FRIEND]: Sending {0} offline and {1} online friends to {2}",
891 friendIDsToReceiveFromOffline.Count, friendIDsToReceiveFromOnline.Count, client.Name);
892 if (friendIDsToReceiveFromOffline.Count > 0) client.SendAgentOffline(friendIDsToReceiveFromOffline.ToArray());
893 if (friendIDsToReceiveFromOnline.Count > 0) client.SendAgentOnline(friendIDsToReceiveFromOnline.ToArray());
894
895 // clear them for a possible second iteration; we don't have to repeat this
896 friendIDsToReceiveFromOffline.Clear();
897 friendIDsToReceiveFromOnline.Clear();
898 }
899
900 // now, send my presence state to my friends
901 for (int i = friendIDsToSendTo.Count - 1; i >= 0; --i)
902 {
903 UUID uuid = friendIDsToSendTo[i];
904 FriendRegionInfo info;
905 if (friendRegions.TryGetValue(uuid, out info) && info.isOnline)
906 {
907 // any client is good enough, root or child...
908 ScenePresence agent = GetAnyPresenceFromAgentID(uuid);
909 if(agent != null)
910 {
911 m_log.DebugFormat("[FRIEND]: Found local agent {0}", agent.Name);
912
913 // friend is online and on this server...
914 if(iAmOnline) agent.ControllingClient.SendAgentOnline(agentArr);
915 else agent.ControllingClient.SendAgentOffline(agentArr);
916
917 // done, remove it
918 friendIDsToSendTo.RemoveAt(i);
919 }
920 }
921 else
922 {
923 m_log.DebugFormat("[FRIEND]: Friend {0} ({1}) is offline; not sending.", uuid, i);
713 924
714 ScenePresence destAgent = GetAnyPresenceFromAgentID(destID); 925 // friend is offline => no need to try sending
715 // inform sender of the card that destination declined the offer 926 friendIDsToSendTo.RemoveAt(i);
716 if (destAgent != null) destAgent.ControllingClient.SendDeclineCallingCard(transactionID); 927 }
928 }
929
930 m_log.DebugFormat("[FRIEND]: Have {0} friends to contact via inter-region comms.", friendIDsToSendTo.Count);
931
932 // we now have all the friends left that are online (we think), but not on this region-server
933 if (friendIDsToSendTo.Count > 0)
934 {
935 // sort them into regions
936 Dictionary<ulong, List<UUID>> friendsInRegion = new Dictionary<ulong,List<UUID>>();
937 foreach (UUID uuid in friendIDsToSendTo)
938 {
939 ulong handle = friendRegions[uuid].regionHandle; // this can't fail as we filtered above already
940 List<UUID> friends;
941 if (!friendsInRegion.TryGetValue(handle, out friends))
942 {
943 friends = new List<UUID>();
944 friendsInRegion[handle] = friends;
945 }
946 friends.Add(uuid);
947 }
948 m_log.DebugFormat("[FRIEND]: Found {0} regions to send to.", friendRegions.Count);
949
950 // clear uuids list and collect missed friends in it for the next retry
951 friendIDsToSendTo.Clear();
952
953 // send bulk updates to the region
954 foreach (KeyValuePair<ulong, List<UUID>> pair in friendsInRegion)
955 {
956 m_log.DebugFormat("[FRIEND]: Inform {0} friends in region {1} that user {2} is {3}line",
957 pair.Value.Count, pair.Key, client.Name, iAmOnline ? "on" : "off");
958
959 friendIDsToSendTo.AddRange(m_initialScene.InformFriendsInOtherRegion(client.AgentId, pair.Key, pair.Value, iAmOnline));
960 }
961 }
962 // now we have in friendIDsToSendTo only the agents left that TPed away while we tried to contact them.
963 // In most cases, it will be empty, and it won't loop here. But sometimes, we have to work harder and try again...
717 } 964 }
718 else m_log.WarnFormat("[CALLING CARD]: Got a DeclineCallingCard from {0} {1} without an offer before.", 965 while(++retry < 3 && friendIDsToSendTo.Count > 0);
719 client.FirstName, client.LastName);
720 } 966 }
721 }
722 967
723 #endregion 968 private void OnEconomyDataRequest(UUID agentID)
969 {
970 // KLUDGE: This is the only way I found to get a message (only) after login was completed and the
971 // client is connected enough to receive UDP packets).
972 // This packet seems to be sent only once, just after connection was established to the first
973 // region after login.
974 // We use it here to trigger a presence update; the old update-on-login was never be heard by
975 // the freshly logged in viewer, as it wasn't connected to the region at that time.
976 // TODO: Feel free to replace this by a better solution if you find one.
977
978 // get the agent. This should work every time, as we just got a packet from it
979 //ScenePresence agent = GetRootPresenceFromAgentID(agentID);
980 // KLUDGE 2: As this is sent quite early, the avatar isn't here as root agent yet. So, we have to cheat a bit
981 ScenePresence agent = GetAnyPresenceFromAgentID(agentID);
982
983 // just to be paranoid...
984 if (agent == null)
985 {
986 m_log.ErrorFormat("[FRIEND]: Got a packet from agent {0} who can't be found anymore!?", agentID);
987 return;
988 }
724 989
725 public struct StoredFriendListUpdate 990 List<FriendListItem> fl;
726 { 991 lock (m_friendLists)
727 public UUID storedFor; 992 {
728 public UUID storedAbout; 993 fl = (List<FriendListItem>)m_friendLists.Get(agent.ControllingClient.AgentId,
729 public bool OnlineYN; 994 m_initialScene.GetFriendList);
995 }
996
997 // tell everyone that we are online
998 SendPresenceState(agent.ControllingClient, fl, true);
999 }
1000
1001 private void OnLogout(IClientAPI remoteClient)
1002 {
1003 m_log.ErrorFormat("[FRIEND]: Client {0} logged out", remoteClient.Name);
1004
1005 List<FriendListItem> fl;
1006 lock (m_friendLists)
1007 {
1008 fl = (List<FriendListItem>)m_friendLists.Get(remoteClient.AgentId,
1009 m_initialScene.GetFriendList);
1010 }
1011
1012 // tell everyone that we are offline
1013 SendPresenceState(remoteClient, fl, false);
1014 }
1015
1016 private void outPending()
1017 {
1018 m_log.DebugFormat("[FRIEND]: got {0} requests pending", m_pendingFriendRequests.Count);
1019 foreach (KeyValuePair<UUID, Transaction> pair in m_pendingFriendRequests)
1020 {
1021 m_log.DebugFormat("[FRIEND]: tid={0}, agent={1}, name={2}, count={3}",
1022 pair.Key, pair.Value.agentID, pair.Value.agentName, pair.Value.count);
1023 }
1024 }
730 } 1025 }
1026
1027 #endregion
731} 1028}
diff --git a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
index adf1103..5597381 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/InstantMessage/InstantMessageModule.cs
@@ -137,8 +137,14 @@ namespace OpenSim.Region.Environment.Modules.Avatar.InstantMessage
137 // IM dialogs need to be pre-processed and have their sessionID filled by the server 137 // IM dialogs need to be pre-processed and have their sessionID filled by the server
138 // so the sim can match the transaction on the return packet. 138 // so the sim can match the transaction on the return packet.
139 139
140 // Don't send a Friend Dialog IM with a UUID.Zero session. 140 // Don't process IMs that are handled elsewhere (e.g. friend dialog
141 if (!dialogHandledElsewhere) 141 // IMs) with a non-UUID.Zero agent session, as those have been send
142 // by a client (either directly or from another region via
143 // inter-region communication) and will be processed in another
144 // module (e.g. the friends-module).
145 // IMs with fromAgentSession == UUID.Zero come from the server, and
146 // have to be passed to the matching viewer
147 if (!dialogHandledElsewhere || fromAgentSession == UUID.Zero)
142 { 148 {
143 // Try root avatar only first 149 // Try root avatar only first
144 foreach (Scene scene in m_scenes) 150 foreach (Scene scene in m_scenes)
diff --git a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
index da7a10c..369b56c 100644
--- a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
+++ b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs
@@ -963,5 +963,9 @@ namespace OpenSim.Region.Environment.Modules.World.NPC
963 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data) 963 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data)
964 { 964 {
965 } 965 }
966
967 public void SendTerminateFriend(UUID exFriendID)
968 {
969 }
966 } 970 }
967} 971}
diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs
index f411a7f..647682e 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.cs
@@ -3200,6 +3200,20 @@ namespace OpenSim.Region.Environment.Scenes
3200 return CommsManager.GetUserFriendList(avatarID); 3200 return CommsManager.GetUserFriendList(avatarID);
3201 } 3201 }
3202 3202
3203 public Dictionary<UUID, FriendRegionInfo> GetFriendRegionInfos(List<UUID> uuids)
3204 {
3205 return CommsManager.GetFriendRegionInfos(uuids);
3206 }
3207
3208 public List<UUID> InformFriendsInOtherRegion(UUID agentId, ulong destRegionHandle, List<UUID> friends, bool online)
3209 {
3210 return CommsManager.InformFriendsInOtherRegion(agentId, destRegionHandle, friends, online);
3211 }
3212
3213 public bool TriggerTerminateFriend(ulong regionHandle, UUID agentID, UUID exFriendID)
3214 {
3215 return CommsManager.TriggerTerminateFriend(regionHandle, agentID, exFriendID);
3216 }
3203 3217
3204 #endregion 3218 #endregion
3205 3219
diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
index b55e5b6..0131109 100644
--- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
+++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs
@@ -962,5 +962,9 @@ namespace OpenSim.Region.Examples.SimpleModule
962 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data) 962 public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data)
963 { 963 {
964 } 964 }
965
966 public void SendTerminateFriend(UUID exFriendID)
967 {
968 }
965 } 969 }
966} 970}