diff options
Diffstat (limited to '')
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 | ||
28 | using System.Collections.Generic; | ||
28 | using OpenMetaverse; | 29 | using OpenMetaverse; |
29 | 30 | ||
30 | namespace OpenSim.Framework.Communications | 31 | namespace 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 | |||
28 | using System.Collections.Generic; | ||
29 | using OpenMetaverse; | ||
30 | |||
31 | namespace 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; | |||
35 | using OpenMetaverse.StructuredData; | 35 | using OpenMetaverse.StructuredData; |
36 | using log4net; | 36 | using log4net; |
37 | using Nwc.XmlRpc; | 37 | using Nwc.XmlRpc; |
38 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Statistics; | 39 | using OpenSim.Framework.Statistics; |
39 | 40 | ||
40 | namespace OpenSim.Framework.Communications | 41 | namespace 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 | |||
28 | using System; | ||
29 | |||
30 | namespace 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; | |||
37 | using OpenSim.Data; | 37 | using OpenSim.Data; |
38 | using OpenSim.Framework; | 38 | using OpenSim.Framework; |
39 | 39 | ||
40 | //using System.Xml; | ||
41 | |||
42 | namespace OpenSim.Grid.MessagingServer | 40 | namespace 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 | ||
40 | namespace OpenSim.Region.Communications.OGS1 | 40 | namespace 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; | |||
34 | using Nini.Config; | 34 | using Nini.Config; |
35 | using Nwc.XmlRpc; | 35 | using Nwc.XmlRpc; |
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Communications.Cache; | ||
38 | using OpenSim.Framework.Servers; | ||
37 | using OpenSim.Region.Environment.Interfaces; | 39 | using OpenSim.Region.Environment.Interfaces; |
38 | using OpenSim.Region.Environment.Scenes; | 40 | using OpenSim.Region.Environment.Scenes; |
39 | 41 | ||
40 | namespace OpenSim.Region.Environment.Modules.Avatar.Friends | 42 | namespace 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 | } |