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: