diff --git a/OpenMetaverse/AvatarManager.cs b/OpenMetaverse/AvatarManager.cs
index 27e45c9f..1855835a 100644
--- a/OpenMetaverse/AvatarManager.cs
+++ b/OpenMetaverse/AvatarManager.cs
@@ -25,16 +25,93 @@
*/
using System;
-using System.Text;
using System.Collections.Generic;
-using System.Threading;
+using System.Text;
+using OpenMetaverse.Http;
using OpenMetaverse.Packets;
using OpenMetaverse.Interfaces;
using OpenMetaverse.Messages.Linden;
+using OpenMetaverse.StructuredData;
namespace OpenMetaverse
{
+ /// Information about agents display name
+ public struct AgentDisplayName
+ {
+ /// Agent UUID
+ public UUID ID;
+ /// Username
+ public string UserName;
+ /// Display name
+ public string DisplayName;
+ /// First name (legacy)
+ public string LegacyFirstName;
+ /// Last name (legacy)
+ public string LegacyLastName;
+ /// Full name (legacy)
+ public string LegacyFullName { get { return string.Format("{0} {1}", LegacyFirstName, LegacyLastName); }}
+ /// Is display name default display name
+ public bool IsDefaultDisplayName;
+ /// Cache display name until
+ public DateTime NextUpdate;
+
+ ///
+ /// Creates AgentDisplayName object from OSD
+ ///
+ /// Incoming OSD data
+ /// AgentDisplayName object
+ public static AgentDisplayName FromOSD(OSD data)
+ {
+ AgentDisplayName ret = new AgentDisplayName();
+
+ OSDMap map = (OSDMap)data;
+ ret.ID = map["id"];
+ ret.UserName = map["username"];
+ ret.DisplayName = map["display_name"];
+ ret.LegacyFirstName = map["legacy_first_name"];
+ ret.LegacyLastName = map["legacy_last_name"];
+ ret.IsDefaultDisplayName = map["is_display_name_default"];
+ ret.NextUpdate = map["display_name_next_update"];
+
+ return ret;
+ }
+
+ ///
+ /// Return object as OSD map
+ ///
+ /// OSD containing agent's display name data
+ public OSD GetOSD()
+ {
+ OSDMap map = new OSDMap();
+
+ map["id"] = ID;
+ map["username"] = UserName;
+ map["display_name"] = DisplayName;
+ map["legacy_first_name"] = LegacyFirstName;
+ map["legacy_last_name"] = LegacyLastName;
+ map["is_display_name_default"] = IsDefaultDisplayName;
+ map["display_name_next_update"] = NextUpdate;
+
+ return map;
+ }
+
+ public override string ToString()
+ {
+ return Helpers.StructToString(this);
+ //StringBuilder result = new StringBuilder();
+ //result.AppendLine();
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "ID", ID, "UUID");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "UserName", UserName, "string");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "DisplayName", DisplayName, "string");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "LegacyFirstName", LegacyFirstName, "string");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "LegaacyLastName", LegaacyLastName, "string");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]" + Environment.NewLine, "IsDefaultDisplayName", IsDefaultDisplayName, "bool");
+ //result.AppendFormat("{0, 30}: {1,-40} [{2}]", "NextUpdate", NextUpdate, "DateTime");
+ //return result.ToString();
+ }
+ }
+
#region Structs
///
/// Holds group information for Avatars such as those you might find in a profile
@@ -114,6 +191,7 @@ namespace OpenMetaverse
{
const int MAX_UUIDS_PER_PACKET = 100;
+ #region Events
/// The event subscribers, null of no subscribers
private EventHandler m_AvatarAnimation;
@@ -450,6 +528,32 @@ namespace OpenMetaverse
remove { lock (m_ClassifiedInfoReplyLock) { m_ClassifiedInfoReply -= value; } }
}
+ /// The event subscribers, null of no subscribers
+ private EventHandler m_DisplayNameUpdate;
+
+ ///Raises the DisplayNameUpdate Event
+ /// A DisplayNameUpdateEventArgs object containing
+ /// the data sent from the simulator
+ protected virtual void OnDisplayNameUpdate(DisplayNameUpdateEventArgs e)
+ {
+ EventHandler handler = m_DisplayNameUpdate;
+ if (handler != null)
+ handler(this, e);
+ }
+
+ /// Thread sync lock object
+ private readonly object m_DisplayNameUpdateLock = new object();
+
+ /// Raised when the simulator sends us data containing
+ /// the details of display name change
+ public event EventHandler DisplayNameUpdate
+ {
+ add { lock (m_DisplayNameUpdateLock) { m_DisplayNameUpdate += value; } }
+ remove { lock (m_DisplayNameUpdateLock) { m_DisplayNameUpdate -= value; } }
+ }
+
+ #endregion Events
+
private GridClient Client;
///
@@ -488,6 +592,8 @@ namespace OpenMetaverse
// Classifieds callbacks
Client.Network.RegisterCallback(PacketType.AvatarClassifiedReply, AvatarClassifiedReplyHandler);
Client.Network.RegisterCallback(PacketType.ClassifiedInfoReply, ClassifiedInfoReplyHandler);
+
+ Client.Network.RegisterEventCallback("DisplayNameUpdate", DisplayNameUpdateMessageHandler);
}
/// Tracks the specified avatar on your map
@@ -557,6 +663,68 @@ namespace OpenMetaverse
}
}
+ ///
+ /// Check if Display Names functionality is available
+ ///
+ /// True if Display name functionality is available
+ public bool DisplayNamesAvailable()
+ {
+ return (Client.Network.CurrentSim != null && Client.Network.CurrentSim.Caps != null) && Client.Network.CurrentSim.Caps.CapabilityURI("GetDisplayNames") != null;
+ }
+
+ ///
+ /// Callback giving results when fetching display names
+ ///
+ /// If the request was successful
+ /// Array of display names
+ /// Array of UUIDs that could not be fetched
+ public delegate void DisplayNamesCallback(bool success, AgentDisplayName[] names, UUID[] badIDs);
+
+ ///
+ /// Request retrieval of display names
+ ///
+ /// List of UUIDs to lookup
+ /// Callback to report result of the operation
+ public void GetDisplayNames(List ids, DisplayNamesCallback callback)
+ {
+ if (!DisplayNamesAvailable() || ids.Count == 0)
+ {
+ callback(false, null, null);
+ }
+
+ StringBuilder query = new StringBuilder();
+ for (int i = 0; i < ids.Count && i < 90; i++)
+ {
+ query.AppendFormat("ids={0}", ids[i]);
+ if (i < ids.Count - 1)
+ {
+ query.Append("&");
+ }
+ }
+
+ Uri uri = new Uri(Client.Network.CurrentSim.Caps.CapabilityURI("GetDisplayNames").AbsoluteUri + "/?" + query);
+
+ CapsClient cap = new CapsClient(uri);
+ cap.OnComplete += (CapsClient client, OSD result, Exception error) =>
+ {
+ try
+ {
+ if (error != null)
+ throw error;
+ GetDisplayNamesMessage msg = new GetDisplayNamesMessage();
+ msg.Deserialize((OSDMap) result);
+ callback(true, msg.Agents, msg.BadIDs);
+ }
+ catch (Exception ex)
+ {
+ Logger.Log("Failed to call GetDisplayNames capability: ",
+ Helpers.LogLevel.Warning, Client, ex);
+ callback(false, null, null);
+ }
+ };
+ cap.BeginGetResponse(null, String.Empty, Client.Settings.CAPS_TIMEOUT);
+ }
+
///
/// Start a request for Avatar Properties
///
@@ -832,6 +1000,21 @@ namespace OpenMetaverse
}
}
+ ///
+ /// EQ Message fired when someone nearby changes their display name
+ ///
+ /// The message key
+ /// the IMessage object containing the deserialized data sent from the simulator
+ /// The which originated the packet
+ protected void DisplayNameUpdateMessageHandler(string capsKey, IMessage message, Simulator simulator)
+ {
+ if (m_DisplayNameUpdate != null)
+ {
+ DisplayNameUpdateMessage msg = (DisplayNameUpdateMessage) message;
+ OnDisplayNameUpdate(new DisplayNameUpdateEventArgs(msg.OldDisplayName, msg.DisplayName));
+ }
+ }
+
///
/// Crossed region handler for message that comes across the EventQueue. Sent to an agent
/// when the agent crosses a sim border into a new region.
@@ -849,7 +1032,7 @@ namespace OpenMetaverse
avatarGroup.AcceptNotices = msg.GroupDataBlock[i].AcceptNotices;
avatarGroup.GroupID = msg.GroupDataBlock[i].GroupID;
avatarGroup.GroupInsigniaID = msg.GroupDataBlock[i].GroupInsigniaID;
- avatarGroup.GroupName = msg.GroupDataBlock[i].GroupName;
+ avatarGroup.GroupName = msg.GroupDataBlock[i].GroupName;
avatarGroup.GroupPowers = msg.GroupDataBlock[i].GroupPowers;
avatarGroup.ListInProfile = msg.NewGroupDataBlock[i].ListInProfile;
@@ -1473,5 +1656,23 @@ namespace OpenMetaverse
this.m_EffectID = id;
}
}
+
+ ///
+ /// Event args class for display name notification messages
+ ///
+ public class DisplayNameUpdateEventArgs : EventArgs
+ {
+ private string oldDisplayName;
+ private AgentDisplayName displayName;
+
+ public string OldDisplayName { get { return oldDisplayName; }}
+ public AgentDisplayName DisplayName { get { return displayName; } }
+
+ public DisplayNameUpdateEventArgs(string oldDisplayName, AgentDisplayName displayName)
+ {
+ this.oldDisplayName = oldDisplayName;
+ this.displayName = displayName;
+ }
+ }
#endregion
}
diff --git a/OpenMetaverse/Messages/LindenMessages.cs b/OpenMetaverse/Messages/LindenMessages.cs
index 0337d91e..0f12a389 100644
--- a/OpenMetaverse/Messages/LindenMessages.cs
+++ b/OpenMetaverse/Messages/LindenMessages.cs
@@ -4767,4 +4767,182 @@ namespace OpenMetaverse.Messages.Linden
}
#endregion Resource usage
+
+ #region Display names
+ ///
+ /// Reply to request for bunch if display names
+ ///
+ public class GetDisplayNamesMessage : IMessage
+ {
+ /// Current display name
+ public AgentDisplayName[] Agents = new AgentDisplayName[0];
+
+ /// Following UUIDs failed to return a valid display name
+ public UUID[] BadIDs = new UUID[0];
+
+ ///
+ /// Serializes the message
+ ///
+ /// OSD containting the messaage
+ public OSDMap Serialize()
+ {
+ OSDArray agents = new OSDArray();
+
+ if (Agents != null && Agents.Length > 0)
+ {
+ for (int i=0; i 0)
+ {
+ for (int i=0; i 0)
+ {
+ Agents = new AgentDisplayName[osdAgents.Count];
+
+ for (int i = 0; i < osdAgents.Count; i++)
+ {
+ Agents[i] = AgentDisplayName.FromOSD(osdAgents[i]);
+ }
+ }
+ }
+
+ if (map["bad_ids"].Type == OSDType.Array)
+ {
+ OSDArray osdBadIDs = (OSDArray) map["bad_ids"];
+ if (osdBadIDs.Count > 0)
+ {
+ BadIDs = new UUID[osdBadIDs.Count];
+
+ for (int i=0; i
+ /// Message sent when requesting change of the display name
+ ///
+ public class SetDisplayNameMessage : IMessage
+ {
+ /// Current display name
+ public string OldDisplayName;
+
+ /// Desired new display name
+ public string NewDisplayName;
+
+ ///
+ /// Serializes the message
+ ///
+ /// OSD containting the messaage
+ public OSDMap Serialize()
+ {
+ OSDArray names = new OSDArray(2) {OldDisplayName, NewDisplayName};
+
+ OSDMap name = new OSDMap();
+ name["display_name"] = names;
+ return name;
+ }
+
+ public void Deserialize(OSDMap map)
+ {
+ OSDArray names = (OSDArray)map["display_name"];
+ OldDisplayName = names[0];
+ NewDisplayName = names[1];
+ }
+ }
+
+ ///
+ /// Message recieved in response to request to change display name
+ ///
+ public class SetDisplayNameReplyMessage : IMessage
+ {
+ /// New display name
+ public AgentDisplayName DisplayName;
+
+ /// String message indicating the result of the operation
+ public string Reason;
+
+ /// Numerical code of the result, 200 indicates success
+ public int Status;
+
+ ///
+ /// Serializes the message
+ ///
+ /// OSD containting the messaage
+ public OSDMap Serialize()
+ {
+ OSDMap agent = (OSDMap)DisplayName.GetOSD();
+ OSDMap ret = new OSDMap();
+ ret["content"] = agent;
+ ret["reason"] = Reason;
+ ret["status"] = Status;
+ return ret;
+ }
+
+ public void Deserialize(OSDMap map)
+ {
+ OSDMap agent = (OSDMap)map["content"];
+ DisplayName = AgentDisplayName.FromOSD(agent);
+ Reason = map["reason"];
+ Status = map["status"];
+ }
+ }
+
+ ///
+ /// Message recieved when someone nearby changes their display name
+ ///
+ public class DisplayNameUpdateMessage : IMessage
+ {
+ /// Previous display name, empty string if default
+ public string OldDisplayName;
+
+ /// New display name
+ public AgentDisplayName DisplayName;
+
+ ///
+ /// Serializes the message
+ ///
+ /// OSD containting the messaage
+ public OSDMap Serialize()
+ {
+ OSDMap agent = (OSDMap)DisplayName.GetOSD();
+ agent["old_display_name"] = OldDisplayName;
+ OSDMap ret = new OSDMap();
+ ret["agent"] = agent;
+ return ret;
+ }
+
+ public void Deserialize(OSDMap map)
+ {
+ OSDMap agent = (OSDMap)map["agent"];
+ DisplayName = AgentDisplayName.FromOSD(agent);
+ OldDisplayName = agent["old_display_name"];
+ }
+ }
+ #endregion Display names
}
diff --git a/OpenMetaverse/Messages/MessageEventDecoder.cs b/OpenMetaverse/Messages/MessageEventDecoder.cs
index 2534919c..aede08cf 100644
--- a/OpenMetaverse/Messages/MessageEventDecoder.cs
+++ b/OpenMetaverse/Messages/MessageEventDecoder.cs
@@ -92,6 +92,10 @@ namespace OpenMetaverse.Messages
case "ObjectMedia": message = new ObjectMediaMessage(); break;
case "AttachmentResources": message = AttachmentResourcesMessage.GetMessageHandler(map); break;
case "LandResources": message = LandResourcesMessage.GetMessageHandler(map); break;
+ case "GetDisplayNames": message = new GetDisplayNamesMessage(); break;
+ case "SetDisplayName": message = new SetDisplayNameMessage(); break;
+ case "SetDisplayNameReply": message = new SetDisplayNameReplyMessage(); break;
+ case "DisplayNameUpdate": message = new DisplayNameUpdateMessage(); break;
//case "ProductInfoRequest": message = new ProductInfoRequestMessage(); break;
// Capabilities TODO: