/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Text; using System.Collections.Generic; using OpenMetaverse.Packets; namespace OpenMetaverse { /// /// /// [Flags] public enum FriendRights : int { /// The avatar has no rights None = 0, /// The avatar can see the online status of the target avatar CanSeeOnline = 1, /// The avatar can see the location of the target avatar on the map CanSeeOnMap = 2, /// The avatar can modify the ojects of the target avatar CanModifyObjects = 4 } /// /// This class holds information about an avatar in the friends list. There are two ways /// to interface to this class. The first is through the set of boolean properties. This is the typical /// way clients of this class will use it. The second interface is through two bitflag properties, /// TheirFriendsRights and MyFriendsRights /// public class FriendInfo { private UUID m_id; private string m_name; private bool m_isOnline; private bool m_canSeeMeOnline; private bool m_canSeeMeOnMap; private bool m_canModifyMyObjects; private bool m_canSeeThemOnline; private bool m_canSeeThemOnMap; private bool m_canModifyTheirObjects; #region Properties /// /// System ID of the avatar /// public UUID UUID { get { return m_id; } } /// /// full name of the avatar /// public string Name { get { return m_name; } set { m_name = value; } } /// /// True if the avatar is online /// public bool IsOnline { get { return m_isOnline; } set { m_isOnline = value; } } /// /// True if the friend can see if I am online /// public bool CanSeeMeOnline { get { return m_canSeeMeOnline; } set { m_canSeeMeOnline = value; // if I can't see them online, then I can't see them on the map if (!m_canSeeMeOnline) m_canSeeMeOnMap = false; } } /// /// True if the friend can see me on the map /// public bool CanSeeMeOnMap { get { return m_canSeeMeOnMap; } set { // if I can't see them online, then I can't see them on the map if (m_canSeeMeOnline) m_canSeeMeOnMap = value; } } /// /// True if the freind can modify my objects /// public bool CanModifyMyObjects { get { return m_canModifyMyObjects; } set { m_canModifyMyObjects = value; } } /// /// True if I can see if my friend is online /// public bool CanSeeThemOnline { get { return m_canSeeThemOnline; } } /// /// True if I can see if my friend is on the map /// public bool CanSeeThemOnMap { get { return m_canSeeThemOnMap; } } /// /// True if I can modify my friend's objects /// public bool CanModifyTheirObjects { get { return m_canModifyTheirObjects; } } /// /// My friend's rights represented as bitmapped flags /// public FriendRights TheirFriendRights { get { FriendRights results = FriendRights.None; if (m_canSeeMeOnline) results |= FriendRights.CanSeeOnline; if (m_canSeeMeOnMap) results |= FriendRights.CanSeeOnMap; if (m_canModifyMyObjects) results |= FriendRights.CanModifyObjects; return results; } set { m_canSeeMeOnline = (value & FriendRights.CanSeeOnline) != 0; m_canSeeMeOnMap = (value & FriendRights.CanSeeOnMap) != 0; m_canModifyMyObjects = (value & FriendRights.CanModifyObjects) != 0; } } /// /// My rights represented as bitmapped flags /// public FriendRights MyFriendRights { get { FriendRights results = FriendRights.None; if (m_canSeeThemOnline) results |= FriendRights.CanSeeOnline; if (m_canSeeThemOnMap) results |= FriendRights.CanSeeOnMap; if (m_canModifyTheirObjects) results |= FriendRights.CanModifyObjects; return results; } set { m_canSeeThemOnline = (value & FriendRights.CanSeeOnline) != 0; m_canSeeThemOnMap = (value & FriendRights.CanSeeOnMap) != 0; m_canModifyTheirObjects = (value & FriendRights.CanModifyObjects) != 0; } } #endregion Properties /// /// Used internally when building the initial list of friends at login time /// /// System ID of the avatar being prepesented /// Rights the friend has to see you online and to modify your objects /// Rights you have to see your friend online and to modify their objects internal FriendInfo(UUID id, FriendRights theirRights, FriendRights myRights) { m_id = id; m_canSeeMeOnline = (theirRights & FriendRights.CanSeeOnline) != 0; m_canSeeMeOnMap = (theirRights & FriendRights.CanSeeOnMap) != 0; m_canModifyMyObjects = (theirRights & FriendRights.CanModifyObjects) != 0; m_canSeeThemOnline = (myRights & FriendRights.CanSeeOnline) != 0; m_canSeeThemOnMap = (myRights & FriendRights.CanSeeOnMap) != 0; m_canModifyTheirObjects = (myRights & FriendRights.CanModifyObjects) != 0; } /// /// FriendInfo represented as a string /// /// A string reprentation of both my rights and my friends rights public override string ToString() { if (!String.IsNullOrEmpty(m_name)) return String.Format("{0} (Their Rights: {1}, My Rights: {2})", m_name, TheirFriendRights, MyFriendRights); else return String.Format("{0} (Their Rights: {1}, My Rights: {2})", m_id, TheirFriendRights, MyFriendRights); } } /// /// This class is used to add and remove avatars from your friends list and to manage their permission. /// public class FriendsManager { #region Delegates /// The event subscribers. null if no subcribers private EventHandler m_FriendOnline; /// Raises the FriendOnline event /// A FriendInfoEventArgs object containing the /// data returned from the data server protected virtual void OnFriendOnline(FriendInfoEventArgs e) { EventHandler handler = m_FriendOnline; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendOnlineLock = new object(); /// Raised when the simulator sends notification one of the members in our friends list comes online public event EventHandler FriendOnline { add { lock (m_FriendOnlineLock) { m_FriendOnline += value; } } remove { lock (m_FriendOnlineLock) { m_FriendOnline -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendOffline; /// Raises the FriendOffline event /// A FriendInfoEventArgs object containing the /// data returned from the data server protected virtual void OnFriendOffline(FriendInfoEventArgs e) { EventHandler handler = m_FriendOffline; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendOfflineLock = new object(); /// Raised when the simulator sends notification one of the members in our friends list goes offline public event EventHandler FriendOffline { add { lock (m_FriendOfflineLock) { m_FriendOffline += value; } } remove { lock (m_FriendOfflineLock) { m_FriendOffline -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendRights; /// Raises the FriendRightsUpdate event /// A FriendInfoEventArgs object containing the /// data returned from the data server protected virtual void OnFriendRights(FriendInfoEventArgs e) { EventHandler handler = m_FriendRights; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendRightsLock = new object(); /// Raised when the simulator sends notification one of the members in our friends list grants or revokes permissions public event EventHandler FriendRightsUpdate { add { lock (m_FriendRightsLock) { m_FriendRights += value; } } remove { lock (m_FriendRightsLock) { m_FriendRights -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendNames; /// Raises the FriendNames event /// A FriendNamesEventArgs object containing the /// data returned from the data server protected virtual void OnFriendNames(FriendNamesEventArgs e) { EventHandler handler = m_FriendNames; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendNamesLock = new object(); /// Raised when the simulator sends us the names on our friends list public event EventHandler FriendNames { add { lock (m_FriendNamesLock) { m_FriendNames += value; } } remove { lock (m_FriendNamesLock) { m_FriendNames -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendshipOffered; /// Raises the FriendshipOffered event /// A FriendshipOfferedEventArgs object containing the /// data returned from the data server protected virtual void OnFriendshipOffered(FriendshipOfferedEventArgs e) { EventHandler handler = m_FriendshipOffered; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendshipOfferedLock = new object(); /// Raised when the simulator sends notification another agent is offering us friendship public event EventHandler FriendshipOffered { add { lock (m_FriendshipOfferedLock) { m_FriendshipOffered += value; } } remove { lock (m_FriendshipOfferedLock) { m_FriendshipOffered -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendshipResponse; /// Raises the FriendshipResponse event /// A FriendshipResponseEventArgs object containing the /// data returned from the data server protected virtual void OnFriendshipResponse(FriendshipResponseEventArgs e) { EventHandler handler = m_FriendshipResponse; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendshipResponseLock = new object(); /// Raised when a request we sent to friend another agent is accepted or declined public event EventHandler FriendshipResponse { add { lock (m_FriendshipResponseLock) { m_FriendshipResponse += value; } } remove { lock (m_FriendshipResponseLock) { m_FriendshipResponse -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendshipTerminated; /// Raises the FriendshipTerminated event /// A FriendshipTerminatedEventArgs object containing the /// data returned from the data server protected virtual void OnFriendshipTerminated(FriendshipTerminatedEventArgs e) { EventHandler handler = m_FriendshipTerminated; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendshipTerminatedLock = new object(); /// Raised when the simulator sends notification one of the members in our friends list has terminated /// our friendship public event EventHandler FriendshipTerminated { add { lock (m_FriendshipTerminatedLock) { m_FriendshipTerminated += value; } } remove { lock (m_FriendshipTerminatedLock) { m_FriendshipTerminated -= value; } } } /// The event subscribers. null if no subcribers private EventHandler m_FriendFound; /// Raises the FriendFoundReply event /// A FriendFoundReplyEventArgs object containing the /// data returned from the data server protected virtual void OnFriendFoundReply(FriendFoundReplyEventArgs e) { EventHandler handler = m_FriendFound; if (handler != null) handler(this, e); } /// Thread sync lock object private readonly object m_FriendFoundLock = new object(); /// Raised when the simulator sends the location of a friend we have /// requested map location info for public event EventHandler FriendFoundReply { add { lock (m_FriendFoundLock) { m_FriendFound += value; } } remove { lock (m_FriendFoundLock) { m_FriendFound -= value; } } } #endregion Delegates #region Events #endregion Events private GridClient Client; /// /// A dictionary of key/value pairs containing known friends of this avatar. /// /// The Key is the of the friend, the value is a /// object that contains detailed information including permissions you have and have given to the friend /// public InternalDictionary FriendList = new InternalDictionary(); /// /// A Dictionary of key/value pairs containing current pending frienship offers. /// /// The key is the of the avatar making the request, /// the value is the of the request which is used to accept /// or decline the friendship offer /// public InternalDictionary FriendRequests = new InternalDictionary(); /// /// Internal constructor /// /// A reference to the GridClient Object internal FriendsManager(GridClient client) { Client = client; Client.Network.LoginProgress += Network_OnConnect; Client.Avatars.UUIDNameReply += new EventHandler(Avatars_OnAvatarNames); Client.Self.IM += Self_IM; Client.Network.RegisterCallback(PacketType.OnlineNotification, OnlineNotificationHandler); Client.Network.RegisterCallback(PacketType.OfflineNotification, OfflineNotificationHandler); Client.Network.RegisterCallback(PacketType.ChangeUserRights, ChangeUserRightsHandler); Client.Network.RegisterCallback(PacketType.TerminateFriendship, TerminateFriendshipHandler); Client.Network.RegisterCallback(PacketType.FindAgent, OnFindAgentReplyHandler); Client.Network.RegisterLoginResponseCallback(new NetworkManager.LoginResponseCallback(Network_OnLoginResponse), new string[] { "buddy-list" }); } #region Public Methods /// /// Accept a friendship request /// /// agentID of avatatar to form friendship with /// imSessionID of the friendship request message public void AcceptFriendship(UUID fromAgentID, UUID imSessionID) { UUID callingCardFolder = Client.Inventory.FindFolderForType(AssetType.CallingCard); AcceptFriendshipPacket request = new AcceptFriendshipPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.TransactionBlock.TransactionID = imSessionID; request.FolderData = new AcceptFriendshipPacket.FolderDataBlock[1]; request.FolderData[0] = new AcceptFriendshipPacket.FolderDataBlock(); request.FolderData[0].FolderID = callingCardFolder; Client.Network.SendPacket(request); FriendInfo friend = new FriendInfo(fromAgentID, FriendRights.CanSeeOnline, FriendRights.CanSeeOnline); if (!FriendList.ContainsKey(fromAgentID)) FriendList.Add(friend.UUID, friend); if (FriendRequests.ContainsKey(fromAgentID)) FriendRequests.Remove(fromAgentID); Client.Avatars.RequestAvatarName(fromAgentID); } /// /// Decline a friendship request /// /// of friend /// imSessionID of the friendship request message public void DeclineFriendship(UUID fromAgentID, UUID imSessionID) { DeclineFriendshipPacket request = new DeclineFriendshipPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.TransactionBlock.TransactionID = imSessionID; Client.Network.SendPacket(request); if (FriendRequests.ContainsKey(fromAgentID)) FriendRequests.Remove(fromAgentID); } /// /// Overload: Offer friendship to an avatar. /// /// System ID of the avatar you are offering friendship to public void OfferFriendship(UUID agentID) { OfferFriendship(agentID, "Do ya wanna be my buddy?"); } /// /// Offer friendship to an avatar. /// /// System ID of the avatar you are offering friendship to /// A message to send with the request public void OfferFriendship(UUID agentID, string message) { Client.Self.InstantMessage(Client.Self.Name, agentID, message, UUID.Random(), InstantMessageDialog.FriendshipOffered, InstantMessageOnline.Offline, Client.Self.SimPosition, Client.Network.CurrentSim.ID, null); } /// /// Terminate a friendship with an avatar /// /// System ID of the avatar you are terminating the friendship with public void TerminateFriendship(UUID agentID) { if (FriendList.ContainsKey(agentID)) { TerminateFriendshipPacket request = new TerminateFriendshipPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.ExBlock.OtherID = agentID; Client.Network.SendPacket(request); if (FriendList.ContainsKey(agentID)) FriendList.Remove(agentID); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data private void TerminateFriendshipHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; TerminateFriendshipPacket itsOver = (TerminateFriendshipPacket)packet; string name = String.Empty; if (FriendList.ContainsKey(itsOver.ExBlock.OtherID)) { name = FriendList[itsOver.ExBlock.OtherID].Name; FriendList.Remove(itsOver.ExBlock.OtherID); } if (m_FriendshipTerminated != null) { OnFriendshipTerminated(new FriendshipTerminatedEventArgs(itsOver.ExBlock.OtherID, name)); } } /// /// Change the rights of a friend avatar. /// /// the of the friend /// the new rights to give the friend /// This method will implicitly set the rights to those passed in the rights parameter. public void GrantRights(UUID friendID, FriendRights rights) { GrantUserRightsPacket request = new GrantUserRightsPacket(); request.AgentData.AgentID = Client.Self.AgentID; request.AgentData.SessionID = Client.Self.SessionID; request.Rights = new GrantUserRightsPacket.RightsBlock[1]; request.Rights[0] = new GrantUserRightsPacket.RightsBlock(); request.Rights[0].AgentRelated = friendID; request.Rights[0].RelatedRights = (int)rights; Client.Network.SendPacket(request); } /// /// Use to map a friends location on the grid. /// /// Friends UUID to find /// public void MapFriend(UUID friendID) { FindAgentPacket stalk = new FindAgentPacket(); stalk.AgentBlock.Hunter = Client.Self.AgentID; stalk.AgentBlock.Prey = friendID; stalk.AgentBlock.SpaceIP = 0; // Will be filled in by the simulator stalk.LocationBlock = new FindAgentPacket.LocationBlockBlock[1]; stalk.LocationBlock[0] = new FindAgentPacket.LocationBlockBlock(); stalk.LocationBlock[0].GlobalX = 0.0; // Filled in by the simulator stalk.LocationBlock[0].GlobalY = 0.0; Client.Network.SendPacket(stalk); } /// /// Use to track a friends movement on the grid /// /// Friends Key public void TrackFriend(UUID friendID) { TrackAgentPacket stalk = new TrackAgentPacket(); stalk.AgentData.AgentID = Client.Self.AgentID; stalk.AgentData.SessionID = Client.Self.SessionID; stalk.TargetData.PreyID = friendID; Client.Network.SendPacket(stalk); } /// /// Ask for a notification of friend's online status /// /// Friend's UUID public void RequestOnlineNotification(UUID friendID) { GenericMessagePacket gmp = new GenericMessagePacket(); gmp.AgentData.AgentID = Client.Self.AgentID; gmp.AgentData.SessionID = Client.Self.SessionID; gmp.AgentData.TransactionID = UUID.Zero; gmp.MethodData.Method = Utils.StringToBytes("requestonlinenotification"); gmp.MethodData.Invoice = UUID.Zero; gmp.ParamList = new GenericMessagePacket.ParamListBlock[1]; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock(); gmp.ParamList[0].Parameter = Utils.StringToBytes(friendID.ToString()); Client.Network.SendPacket(gmp); } #endregion #region Internal events private void Network_OnConnect(object sender, LoginProgressEventArgs e) { if (e.Status != LoginStatus.Success) { return; } List names = new List(); if (FriendList.Count > 0) { FriendList.ForEach( delegate(KeyValuePair kvp) { if (String.IsNullOrEmpty(kvp.Value.Name)) names.Add(kvp.Key); } ); Client.Avatars.RequestAvatarNames(names); } } /// /// This handles the asynchronous response of a RequestAvatarNames call. /// /// /// names cooresponding to the the list of IDs sent the the RequestAvatarNames call. private void Avatars_OnAvatarNames(object sender, UUIDNameReplyEventArgs e) { Dictionary newNames = new Dictionary(); foreach (KeyValuePair kvp in e.Names) { FriendInfo friend; lock (FriendList.Dictionary) { if (FriendList.TryGetValue(kvp.Key, out friend)) { if (friend.Name == null) newNames.Add(kvp.Key, e.Names[kvp.Key]); friend.Name = e.Names[kvp.Key]; FriendList[kvp.Key] = friend; } } } if (newNames.Count > 0 && m_FriendNames != null) { OnFriendNames(new FriendNamesEventArgs(newNames)); } } #endregion #region Packet Handlers /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void OnlineNotificationHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; if (packet.Type == PacketType.OnlineNotification) { OnlineNotificationPacket notification = ((OnlineNotificationPacket)packet); foreach (OnlineNotificationPacket.AgentBlockBlock block in notification.AgentBlock) { FriendInfo friend; lock (FriendList.Dictionary) { if (!FriendList.ContainsKey(block.AgentID)) { friend = new FriendInfo(block.AgentID, FriendRights.CanSeeOnline, FriendRights.CanSeeOnline); FriendList.Add(block.AgentID, friend); } else { friend = FriendList[block.AgentID]; } } bool doNotify = !friend.IsOnline; friend.IsOnline = true; if (m_FriendOnline != null && doNotify) { OnFriendOnline(new FriendInfoEventArgs(friend)); } } } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void OfflineNotificationHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; if (packet.Type == PacketType.OfflineNotification) { OfflineNotificationPacket notification = (OfflineNotificationPacket)packet; foreach (OfflineNotificationPacket.AgentBlockBlock block in notification.AgentBlock) { FriendInfo friend = new FriendInfo(block.AgentID, FriendRights.CanSeeOnline, FriendRights.CanSeeOnline); lock (FriendList.Dictionary) { if (!FriendList.Dictionary.ContainsKey(block.AgentID)) FriendList.Dictionary[block.AgentID] = friend; friend = FriendList.Dictionary[block.AgentID]; } friend.IsOnline = false; if (m_FriendOffline != null) { OnFriendOffline(new FriendInfoEventArgs(friend)); } } } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data private void ChangeUserRightsHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; if (packet.Type == PacketType.ChangeUserRights) { FriendInfo friend; ChangeUserRightsPacket rights = (ChangeUserRightsPacket)packet; foreach (ChangeUserRightsPacket.RightsBlock block in rights.Rights) { FriendRights newRights = (FriendRights)block.RelatedRights; if (FriendList.TryGetValue(block.AgentRelated, out friend)) { friend.TheirFriendRights = newRights; if (m_FriendRights != null) { OnFriendRights(new FriendInfoEventArgs(friend)); } } else if (block.AgentRelated == Client.Self.AgentID) { if (FriendList.TryGetValue(rights.AgentData.AgentID, out friend)) { friend.MyFriendRights = newRights; if (m_FriendRights != null) { OnFriendRights(new FriendInfoEventArgs(friend)); } } } } } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data public void OnFindAgentReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_FriendFound != null) { Packet packet = e.Packet; FindAgentPacket reply = (FindAgentPacket)packet; float x, y; UUID prey = reply.AgentBlock.Prey; ulong regionHandle = Helpers.GlobalPosToRegionHandle((float)reply.LocationBlock[0].GlobalX, (float)reply.LocationBlock[0].GlobalY, out x, out y); Vector3 xyz = new Vector3(x, y, 0f); OnFriendFoundReply(new FriendFoundReplyEventArgs(prey, regionHandle, xyz)); } } #endregion private void Self_IM(object sender, InstantMessageEventArgs e) { if (e.IM.Dialog == InstantMessageDialog.FriendshipOffered) { if (m_FriendshipOffered != null) { if (FriendRequests.ContainsKey(e.IM.FromAgentID)) FriendRequests[e.IM.FromAgentID] = e.IM.IMSessionID; else FriendRequests.Add(e.IM.FromAgentID, e.IM.IMSessionID); OnFriendshipOffered(new FriendshipOfferedEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.IMSessionID)); } } else if (e.IM.Dialog == InstantMessageDialog.FriendshipAccepted) { FriendInfo friend = new FriendInfo(e.IM.FromAgentID, FriendRights.CanSeeOnline, FriendRights.CanSeeOnline); friend.Name = e.IM.FromAgentName; lock (FriendList.Dictionary) FriendList[friend.UUID] = friend; if (m_FriendshipResponse != null) { OnFriendshipResponse(new FriendshipResponseEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, true)); } RequestOnlineNotification(e.IM.FromAgentID); } else if (e.IM.Dialog == InstantMessageDialog.FriendshipDeclined) { if (m_FriendshipResponse != null) { OnFriendshipResponse(new FriendshipResponseEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, false)); } } } /// /// Populate FriendList with data from the login reply /// /// true if login was successful /// true if login request is requiring a redirect /// A string containing the response to the login request /// A string containing the reason for the request /// A object containing the decoded /// reply from the login server private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason, LoginResponseData replyData) { int uuidLength = UUID.Zero.ToString().Length; if (loginSuccess && replyData.BuddyList != null) { foreach (BuddyListEntry buddy in replyData.BuddyList) { UUID bubid; string id = buddy.buddy_id.Length > uuidLength ? buddy.buddy_id.Substring(0, uuidLength) : buddy.buddy_id; if (UUID.TryParse(id, out bubid)) { lock (FriendList.Dictionary) { if (!FriendList.ContainsKey(bubid)) { FriendList[bubid] = new FriendInfo(bubid, (FriendRights)buddy.buddy_rights_given, (FriendRights)buddy.buddy_rights_has); } } } } } } } #region EventArgs /// Contains information on a member of our friends list public class FriendInfoEventArgs : EventArgs { private readonly FriendInfo m_Friend; /// Get the FriendInfo public FriendInfo Friend { get { return m_Friend; } } /// /// Construct a new instance of the FriendInfoEventArgs class /// /// The FriendInfo public FriendInfoEventArgs(FriendInfo friend) { this.m_Friend = friend; } } /// Contains Friend Names public class FriendNamesEventArgs : EventArgs { private readonly Dictionary m_Names; /// A dictionary where the Key is the ID of the Agent, /// and the Value is a string containing their name public Dictionary Names { get { return m_Names; } } /// /// Construct a new instance of the FriendNamesEventArgs class /// /// A dictionary where the Key is the ID of the Agent, /// and the Value is a string containing their name public FriendNamesEventArgs(Dictionary names) { this.m_Names = names; } } /// Sent when another agent requests a friendship with our agent public class FriendshipOfferedEventArgs : EventArgs { private readonly UUID m_AgentID; private readonly string m_AgentName; private readonly UUID m_SessionID; /// Get the ID of the agent requesting friendship public UUID AgentID { get { return m_AgentID; } } /// Get the name of the agent requesting friendship public string AgentName { get { return m_AgentName; } } /// Get the ID of the session, used in accepting or declining the /// friendship offer public UUID SessionID { get { return m_SessionID; } } /// /// Construct a new instance of the FriendshipOfferedEventArgs class /// /// The ID of the agent requesting friendship /// The name of the agent requesting friendship /// The ID of the session, used in accepting or declining the /// friendship offer public FriendshipOfferedEventArgs(UUID agentID, string agentName, UUID imSessionID) { this.m_AgentID = agentID; this.m_AgentName = agentName; this.m_SessionID = imSessionID; } } /// A response containing the results of our request to form a friendship with another agent public class FriendshipResponseEventArgs : EventArgs { private readonly UUID m_AgentID; private readonly string m_AgentName; private readonly bool m_Accepted; /// Get the ID of the agent we requested a friendship with public UUID AgentID { get { return m_AgentID; } } /// Get the name of the agent we requested a friendship with public string AgentName { get { return m_AgentName; } } /// true if the agent accepted our friendship offer public bool Accepted { get { return m_Accepted; } } /// /// Construct a new instance of the FriendShipResponseEventArgs class /// /// The ID of the agent we requested a friendship with /// The name of the agent we requested a friendship with /// true if the agent accepted our friendship offer public FriendshipResponseEventArgs(UUID agentID, string agentName, bool accepted) { this.m_AgentID = agentID; this.m_AgentName = agentName; this.m_Accepted = accepted; } } /// Contains data sent when a friend terminates a friendship with us public class FriendshipTerminatedEventArgs : EventArgs { private readonly UUID m_AgentID; private readonly string m_AgentName; /// Get the ID of the agent that terminated the friendship with us public UUID AgentID { get { return m_AgentID; } } /// Get the name of the agent that terminated the friendship with us public string AgentName { get { return m_AgentName; } } /// /// Construct a new instance of the FrindshipTerminatedEventArgs class /// /// The ID of the friend who terminated the friendship with us /// The name of the friend who terminated the friendship with us public FriendshipTerminatedEventArgs(UUID agentID, string agentName) { this.m_AgentID = agentID; this.m_AgentName = agentName; } } /// /// Data sent in response to a request which contains the information to allow us to map the friends location /// public class FriendFoundReplyEventArgs : EventArgs { private readonly UUID m_AgentID; private readonly ulong m_RegionHandle; private readonly Vector3 m_Location; /// Get the ID of the agent we have received location information for public UUID AgentID { get { return m_AgentID; } } /// Get the region handle where our mapped friend is located public ulong RegionHandle { get { return m_RegionHandle; } } /// Get the simulator local position where our friend is located public Vector3 Location { get { return m_Location; } } /// /// Construct a new instance of the FriendFoundReplyEventArgs class /// /// The ID of the agent we have requested location information for /// The region handle where our friend is located /// The simulator local position our friend is located public FriendFoundReplyEventArgs(UUID agentID, ulong regionHandle, Vector3 location) { this.m_AgentID = agentID; this.m_RegionHandle = regionHandle; this.m_Location = location; } } #endregion }