PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenMetaverse/Modules/FriendsManager.cs

https://bitbucket.org/VirtualReality/3rdparty-addon-modules
C# | 1076 lines | 653 code | 144 blank | 279 comment | 99 complexity | c71ce3e0df52740a15c55e6e6b19e30b MD5 | raw file
  1. /*
  2. * Copyright (c) 2007-2008, openmetaverse.org
  3. * All rights reserved.
  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 notice, this
  8. * list of conditions and the following disclaimer.
  9. * - Neither the name of the openmetaverse.org nor the names
  10. * of its contributors may be used to endorse or promote products derived from
  11. * this software without specific prior written permission.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  17. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23. * POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. using System;
  26. using System.Text;
  27. using System.Collections.Generic;
  28. using OpenMetaverse.Packets;
  29. namespace OpenMetaverse
  30. {
  31. /// <summary>
  32. ///
  33. /// </summary>
  34. [Flags]
  35. public enum FriendRights : int
  36. {
  37. /// <summary>The avatar has no rights</summary>
  38. None = 0,
  39. /// <summary>The avatar can see the online status of the target avatar</summary>
  40. CanSeeOnline = 1,
  41. /// <summary>The avatar can see the location of the target avatar on the map</summary>
  42. CanSeeOnMap = 2,
  43. /// <summary>The avatar can modify the ojects of the target avatar </summary>
  44. CanModifyObjects = 4
  45. }
  46. /// <summary>
  47. /// This class holds information about an avatar in the friends list. There are two ways
  48. /// to interface to this class. The first is through the set of boolean properties. This is the typical
  49. /// way clients of this class will use it. The second interface is through two bitflag properties,
  50. /// TheirFriendsRights and MyFriendsRights
  51. /// </summary>
  52. public class FriendInfo
  53. {
  54. private UUID m_id;
  55. private string m_name;
  56. private bool m_isOnline;
  57. private bool m_canSeeMeOnline;
  58. private bool m_canSeeMeOnMap;
  59. private bool m_canModifyMyObjects;
  60. private bool m_canSeeThemOnline;
  61. private bool m_canSeeThemOnMap;
  62. private bool m_canModifyTheirObjects;
  63. #region Properties
  64. /// <summary>
  65. /// System ID of the avatar
  66. /// </summary>
  67. public UUID UUID { get { return m_id; } }
  68. /// <summary>
  69. /// full name of the avatar
  70. /// </summary>
  71. public string Name
  72. {
  73. get { return m_name; }
  74. set { m_name = value; }
  75. }
  76. /// <summary>
  77. /// True if the avatar is online
  78. /// </summary>
  79. public bool IsOnline
  80. {
  81. get { return m_isOnline; }
  82. set { m_isOnline = value; }
  83. }
  84. /// <summary>
  85. /// True if the friend can see if I am online
  86. /// </summary>
  87. public bool CanSeeMeOnline
  88. {
  89. get { return m_canSeeMeOnline; }
  90. set
  91. {
  92. m_canSeeMeOnline = value;
  93. // if I can't see them online, then I can't see them on the map
  94. if (!m_canSeeMeOnline)
  95. m_canSeeMeOnMap = false;
  96. }
  97. }
  98. /// <summary>
  99. /// True if the friend can see me on the map
  100. /// </summary>
  101. public bool CanSeeMeOnMap
  102. {
  103. get { return m_canSeeMeOnMap; }
  104. set
  105. {
  106. // if I can't see them online, then I can't see them on the map
  107. if (m_canSeeMeOnline)
  108. m_canSeeMeOnMap = value;
  109. }
  110. }
  111. /// <summary>
  112. /// True if the freind can modify my objects
  113. /// </summary>
  114. public bool CanModifyMyObjects
  115. {
  116. get { return m_canModifyMyObjects; }
  117. set { m_canModifyMyObjects = value; }
  118. }
  119. /// <summary>
  120. /// True if I can see if my friend is online
  121. /// </summary>
  122. public bool CanSeeThemOnline { get { return m_canSeeThemOnline; } }
  123. /// <summary>
  124. /// True if I can see if my friend is on the map
  125. /// </summary>
  126. public bool CanSeeThemOnMap { get { return m_canSeeThemOnMap; } }
  127. /// <summary>
  128. /// True if I can modify my friend's objects
  129. /// </summary>
  130. public bool CanModifyTheirObjects { get { return m_canModifyTheirObjects; } }
  131. /// <summary>
  132. /// My friend's rights represented as bitmapped flags
  133. /// </summary>
  134. public FriendRights TheirFriendRights
  135. {
  136. get
  137. {
  138. FriendRights results = FriendRights.None;
  139. if (m_canSeeMeOnline)
  140. results |= FriendRights.CanSeeOnline;
  141. if (m_canSeeMeOnMap)
  142. results |= FriendRights.CanSeeOnMap;
  143. if (m_canModifyMyObjects)
  144. results |= FriendRights.CanModifyObjects;
  145. return results;
  146. }
  147. set
  148. {
  149. m_canSeeMeOnline = (value & FriendRights.CanSeeOnline) != 0;
  150. m_canSeeMeOnMap = (value & FriendRights.CanSeeOnMap) != 0;
  151. m_canModifyMyObjects = (value & FriendRights.CanModifyObjects) != 0;
  152. }
  153. }
  154. /// <summary>
  155. /// My rights represented as bitmapped flags
  156. /// </summary>
  157. public FriendRights MyFriendRights
  158. {
  159. get
  160. {
  161. FriendRights results = FriendRights.None;
  162. if (m_canSeeThemOnline)
  163. results |= FriendRights.CanSeeOnline;
  164. if (m_canSeeThemOnMap)
  165. results |= FriendRights.CanSeeOnMap;
  166. if (m_canModifyTheirObjects)
  167. results |= FriendRights.CanModifyObjects;
  168. return results;
  169. }
  170. set
  171. {
  172. m_canSeeThemOnline = (value & FriendRights.CanSeeOnline) != 0;
  173. m_canSeeThemOnMap = (value & FriendRights.CanSeeOnMap) != 0;
  174. m_canModifyTheirObjects = (value & FriendRights.CanModifyObjects) != 0;
  175. }
  176. }
  177. #endregion Properties
  178. /// <summary>
  179. /// Used internally when building the initial list of friends at login time
  180. /// </summary>
  181. /// <param name="id">System ID of the avatar being prepesented</param>
  182. /// <param name="theirRights">Rights the friend has to see you online and to modify your objects</param>
  183. /// <param name="myRights">Rights you have to see your friend online and to modify their objects</param>
  184. internal FriendInfo(UUID id, FriendRights theirRights, FriendRights myRights)
  185. {
  186. m_id = id;
  187. m_canSeeMeOnline = (theirRights & FriendRights.CanSeeOnline) != 0;
  188. m_canSeeMeOnMap = (theirRights & FriendRights.CanSeeOnMap) != 0;
  189. m_canModifyMyObjects = (theirRights & FriendRights.CanModifyObjects) != 0;
  190. m_canSeeThemOnline = (myRights & FriendRights.CanSeeOnline) != 0;
  191. m_canSeeThemOnMap = (myRights & FriendRights.CanSeeOnMap) != 0;
  192. m_canModifyTheirObjects = (myRights & FriendRights.CanModifyObjects) != 0;
  193. }
  194. /// <summary>
  195. /// FriendInfo represented as a string
  196. /// </summary>
  197. /// <returns>A string reprentation of both my rights and my friends rights</returns>
  198. public override string ToString()
  199. {
  200. if (!String.IsNullOrEmpty(m_name))
  201. return String.Format("{0} (Their Rights: {1}, My Rights: {2})", m_name, TheirFriendRights,
  202. MyFriendRights);
  203. else
  204. return String.Format("{0} (Their Rights: {1}, My Rights: {2})", m_id, TheirFriendRights,
  205. MyFriendRights);
  206. }
  207. }
  208. /// <summary>
  209. /// This class is used to add and remove avatars from your friends list and to manage their permission.
  210. /// </summary>
  211. public class FriendsManager
  212. {
  213. #region Delegates
  214. /// <summary>The event subscribers. null if no subcribers</summary>
  215. private EventHandler<FriendInfoEventArgs> m_FriendOnline;
  216. /// <summary>Raises the FriendOnline event</summary>
  217. /// <param name="e">A FriendInfoEventArgs object containing the
  218. /// data returned from the data server</param>
  219. protected virtual void OnFriendOnline(FriendInfoEventArgs e)
  220. {
  221. EventHandler<FriendInfoEventArgs> handler = m_FriendOnline;
  222. if (handler != null)
  223. handler(this, e);
  224. }
  225. /// <summary>Thread sync lock object</summary>
  226. private readonly object m_FriendOnlineLock = new object();
  227. /// <summary>Raised when the simulator sends notification one of the members in our friends list comes online</summary>
  228. public event EventHandler<FriendInfoEventArgs> FriendOnline
  229. {
  230. add { lock (m_FriendOnlineLock) { m_FriendOnline += value; } }
  231. remove { lock (m_FriendOnlineLock) { m_FriendOnline -= value; } }
  232. }
  233. /// <summary>The event subscribers. null if no subcribers</summary>
  234. private EventHandler<FriendInfoEventArgs> m_FriendOffline;
  235. /// <summary>Raises the FriendOffline event</summary>
  236. /// <param name="e">A FriendInfoEventArgs object containing the
  237. /// data returned from the data server</param>
  238. protected virtual void OnFriendOffline(FriendInfoEventArgs e)
  239. {
  240. EventHandler<FriendInfoEventArgs> handler = m_FriendOffline;
  241. if (handler != null)
  242. handler(this, e);
  243. }
  244. /// <summary>Thread sync lock object</summary>
  245. private readonly object m_FriendOfflineLock = new object();
  246. /// <summary>Raised when the simulator sends notification one of the members in our friends list goes offline</summary>
  247. public event EventHandler<FriendInfoEventArgs> FriendOffline
  248. {
  249. add { lock (m_FriendOfflineLock) { m_FriendOffline += value; } }
  250. remove { lock (m_FriendOfflineLock) { m_FriendOffline -= value; } }
  251. }
  252. /// <summary>The event subscribers. null if no subcribers</summary>
  253. private EventHandler<FriendInfoEventArgs> m_FriendRights;
  254. /// <summary>Raises the FriendRightsUpdate event</summary>
  255. /// <param name="e">A FriendInfoEventArgs object containing the
  256. /// data returned from the data server</param>
  257. protected virtual void OnFriendRights(FriendInfoEventArgs e)
  258. {
  259. EventHandler<FriendInfoEventArgs> handler = m_FriendRights;
  260. if (handler != null)
  261. handler(this, e);
  262. }
  263. /// <summary>Thread sync lock object</summary>
  264. private readonly object m_FriendRightsLock = new object();
  265. /// <summary>Raised when the simulator sends notification one of the members in our friends list grants or revokes permissions</summary>
  266. public event EventHandler<FriendInfoEventArgs> FriendRightsUpdate
  267. {
  268. add { lock (m_FriendRightsLock) { m_FriendRights += value; } }
  269. remove { lock (m_FriendRightsLock) { m_FriendRights -= value; } }
  270. }
  271. /// <summary>The event subscribers. null if no subcribers</summary>
  272. private EventHandler<FriendNamesEventArgs> m_FriendNames;
  273. /// <summary>Raises the FriendNames event</summary>
  274. /// <param name="e">A FriendNamesEventArgs object containing the
  275. /// data returned from the data server</param>
  276. protected virtual void OnFriendNames(FriendNamesEventArgs e)
  277. {
  278. EventHandler<FriendNamesEventArgs> handler = m_FriendNames;
  279. if (handler != null)
  280. handler(this, e);
  281. }
  282. /// <summary>Thread sync lock object</summary>
  283. private readonly object m_FriendNamesLock = new object();
  284. /// <summary>Raised when the simulator sends us the names on our friends list</summary>
  285. public event EventHandler<FriendNamesEventArgs> FriendNames
  286. {
  287. add { lock (m_FriendNamesLock) { m_FriendNames += value; } }
  288. remove { lock (m_FriendNamesLock) { m_FriendNames -= value; } }
  289. }
  290. /// <summary>The event subscribers. null if no subcribers</summary>
  291. private EventHandler<FriendshipOfferedEventArgs> m_FriendshipOffered;
  292. /// <summary>Raises the FriendshipOffered event</summary>
  293. /// <param name="e">A FriendshipOfferedEventArgs object containing the
  294. /// data returned from the data server</param>
  295. protected virtual void OnFriendshipOffered(FriendshipOfferedEventArgs e)
  296. {
  297. EventHandler<FriendshipOfferedEventArgs> handler = m_FriendshipOffered;
  298. if (handler != null)
  299. handler(this, e);
  300. }
  301. /// <summary>Thread sync lock object</summary>
  302. private readonly object m_FriendshipOfferedLock = new object();
  303. /// <summary>Raised when the simulator sends notification another agent is offering us friendship</summary>
  304. public event EventHandler<FriendshipOfferedEventArgs> FriendshipOffered
  305. {
  306. add { lock (m_FriendshipOfferedLock) { m_FriendshipOffered += value; } }
  307. remove { lock (m_FriendshipOfferedLock) { m_FriendshipOffered -= value; } }
  308. }
  309. /// <summary>The event subscribers. null if no subcribers</summary>
  310. private EventHandler<FriendshipResponseEventArgs> m_FriendshipResponse;
  311. /// <summary>Raises the FriendshipResponse event</summary>
  312. /// <param name="e">A FriendshipResponseEventArgs object containing the
  313. /// data returned from the data server</param>
  314. protected virtual void OnFriendshipResponse(FriendshipResponseEventArgs e)
  315. {
  316. EventHandler<FriendshipResponseEventArgs> handler = m_FriendshipResponse;
  317. if (handler != null)
  318. handler(this, e);
  319. }
  320. /// <summary>Thread sync lock object</summary>
  321. private readonly object m_FriendshipResponseLock = new object();
  322. /// <summary>Raised when a request we sent to friend another agent is accepted or declined</summary>
  323. public event EventHandler<FriendshipResponseEventArgs> FriendshipResponse
  324. {
  325. add { lock (m_FriendshipResponseLock) { m_FriendshipResponse += value; } }
  326. remove { lock (m_FriendshipResponseLock) { m_FriendshipResponse -= value; } }
  327. }
  328. /// <summary>The event subscribers. null if no subcribers</summary>
  329. private EventHandler<FriendshipTerminatedEventArgs> m_FriendshipTerminated;
  330. /// <summary>Raises the FriendshipTerminated event</summary>
  331. /// <param name="e">A FriendshipTerminatedEventArgs object containing the
  332. /// data returned from the data server</param>
  333. protected virtual void OnFriendshipTerminated(FriendshipTerminatedEventArgs e)
  334. {
  335. EventHandler<FriendshipTerminatedEventArgs> handler = m_FriendshipTerminated;
  336. if (handler != null)
  337. handler(this, e);
  338. }
  339. /// <summary>Thread sync lock object</summary>
  340. private readonly object m_FriendshipTerminatedLock = new object();
  341. /// <summary>Raised when the simulator sends notification one of the members in our friends list has terminated
  342. /// our friendship</summary>
  343. public event EventHandler<FriendshipTerminatedEventArgs> FriendshipTerminated
  344. {
  345. add { lock (m_FriendshipTerminatedLock) { m_FriendshipTerminated += value; } }
  346. remove { lock (m_FriendshipTerminatedLock) { m_FriendshipTerminated -= value; } }
  347. }
  348. /// <summary>The event subscribers. null if no subcribers</summary>
  349. private EventHandler<FriendFoundReplyEventArgs> m_FriendFound;
  350. /// <summary>Raises the FriendFoundReply event</summary>
  351. /// <param name="e">A FriendFoundReplyEventArgs object containing the
  352. /// data returned from the data server</param>
  353. protected virtual void OnFriendFoundReply(FriendFoundReplyEventArgs e)
  354. {
  355. EventHandler<FriendFoundReplyEventArgs> handler = m_FriendFound;
  356. if (handler != null)
  357. handler(this, e);
  358. }
  359. /// <summary>Thread sync lock object</summary>
  360. private readonly object m_FriendFoundLock = new object();
  361. /// <summary>Raised when the simulator sends the location of a friend we have
  362. /// requested map location info for</summary>
  363. public event EventHandler<FriendFoundReplyEventArgs> FriendFoundReply
  364. {
  365. add { lock (m_FriendFoundLock) { m_FriendFound += value; } }
  366. remove { lock (m_FriendFoundLock) { m_FriendFound -= value; } }
  367. }
  368. #endregion Delegates
  369. #region Events
  370. #endregion Events
  371. private GridClient Client;
  372. /// <summary>
  373. /// A dictionary of key/value pairs containing known friends of this avatar.
  374. ///
  375. /// The Key is the <seealso cref="UUID"/> of the friend, the value is a <seealso cref="FriendInfo"/>
  376. /// object that contains detailed information including permissions you have and have given to the friend
  377. /// </summary>
  378. public InternalDictionary<UUID, FriendInfo> FriendList = new InternalDictionary<UUID, FriendInfo>();
  379. /// <summary>
  380. /// A Dictionary of key/value pairs containing current pending frienship offers.
  381. ///
  382. /// The key is the <seealso cref="UUID"/> of the avatar making the request,
  383. /// the value is the <seealso cref="UUID"/> of the request which is used to accept
  384. /// or decline the friendship offer
  385. /// </summary>
  386. public InternalDictionary<UUID, UUID> FriendRequests = new InternalDictionary<UUID, UUID>();
  387. /// <summary>
  388. /// Internal constructor
  389. /// </summary>
  390. /// <param name="client">A reference to the GridClient Object</param>
  391. internal FriendsManager(GridClient client)
  392. {
  393. Client = client;
  394. Client.Network.LoginProgress += Network_OnConnect;
  395. Client.Avatars.UUIDNameReply += new EventHandler<UUIDNameReplyEventArgs>(Avatars_OnAvatarNames);
  396. Client.Self.IM += Self_IM;
  397. Client.Network.RegisterCallback(PacketType.OnlineNotification, OnlineNotificationHandler);
  398. Client.Network.RegisterCallback(PacketType.OfflineNotification, OfflineNotificationHandler);
  399. Client.Network.RegisterCallback(PacketType.ChangeUserRights, ChangeUserRightsHandler);
  400. Client.Network.RegisterCallback(PacketType.TerminateFriendship, TerminateFriendshipHandler);
  401. Client.Network.RegisterCallback(PacketType.FindAgent, OnFindAgentReplyHandler);
  402. Client.Network.RegisterLoginResponseCallback(new NetworkManager.LoginResponseCallback(Network_OnLoginResponse),
  403. new string[] { "buddy-list" });
  404. }
  405. #region Public Methods
  406. /// <summary>
  407. /// Accept a friendship request
  408. /// </summary>
  409. /// <param name="fromAgentID">agentID of avatatar to form friendship with</param>
  410. /// <param name="imSessionID">imSessionID of the friendship request message</param>
  411. public void AcceptFriendship(UUID fromAgentID, UUID imSessionID)
  412. {
  413. UUID callingCardFolder = Client.Inventory.FindFolderForType(AssetType.CallingCard);
  414. AcceptFriendshipPacket request = new AcceptFriendshipPacket();
  415. request.AgentData.AgentID = Client.Self.AgentID;
  416. request.AgentData.SessionID = Client.Self.SessionID;
  417. request.TransactionBlock.TransactionID = imSessionID;
  418. request.FolderData = new AcceptFriendshipPacket.FolderDataBlock[1];
  419. request.FolderData[0] = new AcceptFriendshipPacket.FolderDataBlock();
  420. request.FolderData[0].FolderID = callingCardFolder;
  421. Client.Network.SendPacket(request);
  422. FriendInfo friend = new FriendInfo(fromAgentID, FriendRights.CanSeeOnline,
  423. FriendRights.CanSeeOnline);
  424. if (!FriendList.ContainsKey(fromAgentID))
  425. FriendList.Add(friend.UUID, friend);
  426. if (FriendRequests.ContainsKey(fromAgentID))
  427. FriendRequests.Remove(fromAgentID);
  428. Client.Avatars.RequestAvatarName(fromAgentID);
  429. }
  430. /// <summary>
  431. /// Decline a friendship request
  432. /// </summary>
  433. /// <param name="fromAgentID"><seealso cref="UUID"/> of friend</param>
  434. /// <param name="imSessionID">imSessionID of the friendship request message</param>
  435. public void DeclineFriendship(UUID fromAgentID, UUID imSessionID)
  436. {
  437. DeclineFriendshipPacket request = new DeclineFriendshipPacket();
  438. request.AgentData.AgentID = Client.Self.AgentID;
  439. request.AgentData.SessionID = Client.Self.SessionID;
  440. request.TransactionBlock.TransactionID = imSessionID;
  441. Client.Network.SendPacket(request);
  442. if (FriendRequests.ContainsKey(fromAgentID))
  443. FriendRequests.Remove(fromAgentID);
  444. }
  445. /// <summary>
  446. /// Overload: Offer friendship to an avatar.
  447. /// </summary>
  448. /// <param name="agentID">System ID of the avatar you are offering friendship to</param>
  449. public void OfferFriendship(UUID agentID)
  450. {
  451. OfferFriendship(agentID, "Do ya wanna be my buddy?");
  452. }
  453. /// <summary>
  454. /// Offer friendship to an avatar.
  455. /// </summary>
  456. /// <param name="agentID">System ID of the avatar you are offering friendship to</param>
  457. /// <param name="message">A message to send with the request</param>
  458. public void OfferFriendship(UUID agentID, string message)
  459. {
  460. Client.Self.InstantMessage(Client.Self.Name,
  461. agentID,
  462. message,
  463. UUID.Random(),
  464. InstantMessageDialog.FriendshipOffered,
  465. InstantMessageOnline.Offline,
  466. Client.Self.SimPosition,
  467. Client.Network.CurrentSim.ID,
  468. null);
  469. }
  470. /// <summary>
  471. /// Terminate a friendship with an avatar
  472. /// </summary>
  473. /// <param name="agentID">System ID of the avatar you are terminating the friendship with</param>
  474. public void TerminateFriendship(UUID agentID)
  475. {
  476. if (FriendList.ContainsKey(agentID))
  477. {
  478. TerminateFriendshipPacket request = new TerminateFriendshipPacket();
  479. request.AgentData.AgentID = Client.Self.AgentID;
  480. request.AgentData.SessionID = Client.Self.SessionID;
  481. request.ExBlock.OtherID = agentID;
  482. Client.Network.SendPacket(request);
  483. if (FriendList.ContainsKey(agentID))
  484. FriendList.Remove(agentID);
  485. }
  486. }
  487. /// <summary>Process an incoming packet and raise the appropriate events</summary>
  488. /// <param name="sender">The sender</param>
  489. /// <param name="e">The EventArgs object containing the packet data</param>
  490. private void TerminateFriendshipHandler(object sender, PacketReceivedEventArgs e)
  491. {
  492. Packet packet = e.Packet;
  493. TerminateFriendshipPacket itsOver = (TerminateFriendshipPacket)packet;
  494. string name = String.Empty;
  495. if (FriendList.ContainsKey(itsOver.ExBlock.OtherID))
  496. {
  497. name = FriendList[itsOver.ExBlock.OtherID].Name;
  498. FriendList.Remove(itsOver.ExBlock.OtherID);
  499. }
  500. if (m_FriendshipTerminated != null)
  501. {
  502. OnFriendshipTerminated(new FriendshipTerminatedEventArgs(itsOver.ExBlock.OtherID, name));
  503. }
  504. }
  505. /// <summary>
  506. /// Change the rights of a friend avatar.
  507. /// </summary>
  508. /// <param name="friendID">the <seealso cref="UUID"/> of the friend</param>
  509. /// <param name="rights">the new rights to give the friend</param>
  510. /// <remarks>This method will implicitly set the rights to those passed in the rights parameter.</remarks>
  511. public void GrantRights(UUID friendID, FriendRights rights)
  512. {
  513. GrantUserRightsPacket request = new GrantUserRightsPacket();
  514. request.AgentData.AgentID = Client.Self.AgentID;
  515. request.AgentData.SessionID = Client.Self.SessionID;
  516. request.Rights = new GrantUserRightsPacket.RightsBlock[1];
  517. request.Rights[0] = new GrantUserRightsPacket.RightsBlock();
  518. request.Rights[0].AgentRelated = friendID;
  519. request.Rights[0].RelatedRights = (int)rights;
  520. Client.Network.SendPacket(request);
  521. }
  522. /// <summary>
  523. /// Use to map a friends location on the grid.
  524. /// </summary>
  525. /// <param name="friendID">Friends UUID to find</param>
  526. /// <remarks><seealso cref="E:OnFriendFound"/></remarks>
  527. public void MapFriend(UUID friendID)
  528. {
  529. FindAgentPacket stalk = new FindAgentPacket();
  530. stalk.AgentBlock.Hunter = Client.Self.AgentID;
  531. stalk.AgentBlock.Prey = friendID;
  532. stalk.AgentBlock.SpaceIP = 0; // Will be filled in by the simulator
  533. stalk.LocationBlock = new FindAgentPacket.LocationBlockBlock[1];
  534. stalk.LocationBlock[0] = new FindAgentPacket.LocationBlockBlock();
  535. stalk.LocationBlock[0].GlobalX = 0.0; // Filled in by the simulator
  536. stalk.LocationBlock[0].GlobalY = 0.0;
  537. Client.Network.SendPacket(stalk);
  538. }
  539. /// <summary>
  540. /// Use to track a friends movement on the grid
  541. /// </summary>
  542. /// <param name="friendID">Friends Key</param>
  543. public void TrackFriend(UUID friendID)
  544. {
  545. TrackAgentPacket stalk = new TrackAgentPacket();
  546. stalk.AgentData.AgentID = Client.Self.AgentID;
  547. stalk.AgentData.SessionID = Client.Self.SessionID;
  548. stalk.TargetData.PreyID = friendID;
  549. Client.Network.SendPacket(stalk);
  550. }
  551. /// <summary>
  552. /// Ask for a notification of friend's online status
  553. /// </summary>
  554. /// <param name="friendID">Friend's UUID</param>
  555. public void RequestOnlineNotification(UUID friendID)
  556. {
  557. GenericMessagePacket gmp = new GenericMessagePacket();
  558. gmp.AgentData.AgentID = Client.Self.AgentID;
  559. gmp.AgentData.SessionID = Client.Self.SessionID;
  560. gmp.AgentData.TransactionID = UUID.Zero;
  561. gmp.MethodData.Method = Utils.StringToBytes("requestonlinenotification");
  562. gmp.MethodData.Invoice = UUID.Zero;
  563. gmp.ParamList = new GenericMessagePacket.ParamListBlock[1];
  564. gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock();
  565. gmp.ParamList[0].Parameter = Utils.StringToBytes(friendID.ToString());
  566. Client.Network.SendPacket(gmp);
  567. }
  568. #endregion
  569. #region Internal events
  570. private void Network_OnConnect(object sender, LoginProgressEventArgs e)
  571. {
  572. if (e.Status != LoginStatus.Success)
  573. {
  574. return;
  575. }
  576. List<UUID> names = new List<UUID>();
  577. if (FriendList.Count > 0)
  578. {
  579. FriendList.ForEach(
  580. delegate(KeyValuePair<UUID, FriendInfo> kvp)
  581. {
  582. if (String.IsNullOrEmpty(kvp.Value.Name))
  583. names.Add(kvp.Key);
  584. }
  585. );
  586. Client.Avatars.RequestAvatarNames(names);
  587. }
  588. }
  589. /// <summary>
  590. /// This handles the asynchronous response of a RequestAvatarNames call.
  591. /// </summary>
  592. /// <param name="sender"></param>
  593. /// <param name="e">names cooresponding to the the list of IDs sent the the RequestAvatarNames call.</param>
  594. private void Avatars_OnAvatarNames(object sender, UUIDNameReplyEventArgs e)
  595. {
  596. Dictionary<UUID, string> newNames = new Dictionary<UUID, string>();
  597. foreach (KeyValuePair<UUID, string> kvp in e.Names)
  598. {
  599. FriendInfo friend;
  600. lock (FriendList.Dictionary)
  601. {
  602. if (FriendList.TryGetValue(kvp.Key, out friend))
  603. {
  604. if (friend.Name == null)
  605. newNames.Add(kvp.Key, e.Names[kvp.Key]);
  606. friend.Name = e.Names[kvp.Key];
  607. FriendList[kvp.Key] = friend;
  608. }
  609. }
  610. }
  611. if (newNames.Count > 0 && m_FriendNames != null)
  612. {
  613. OnFriendNames(new FriendNamesEventArgs(newNames));
  614. }
  615. }
  616. #endregion
  617. #region Packet Handlers
  618. /// <summary>Process an incoming packet and raise the appropriate events</summary>
  619. /// <param name="sender">The sender</param>
  620. /// <param name="e">The EventArgs object containing the packet data</param>
  621. protected void OnlineNotificationHandler(object sender, PacketReceivedEventArgs e)
  622. {
  623. Packet packet = e.Packet;
  624. if (packet.Type == PacketType.OnlineNotification)
  625. {
  626. OnlineNotificationPacket notification = ((OnlineNotificationPacket)packet);
  627. foreach (OnlineNotificationPacket.AgentBlockBlock block in notification.AgentBlock)
  628. {
  629. FriendInfo friend;
  630. lock (FriendList.Dictionary)
  631. {
  632. if (!FriendList.ContainsKey(block.AgentID))
  633. {
  634. friend = new FriendInfo(block.AgentID, FriendRights.CanSeeOnline,
  635. FriendRights.CanSeeOnline);
  636. FriendList.Add(block.AgentID, friend);
  637. }
  638. else
  639. {
  640. friend = FriendList[block.AgentID];
  641. }
  642. }
  643. bool doNotify = !friend.IsOnline;
  644. friend.IsOnline = true;
  645. if (m_FriendOnline != null && doNotify)
  646. {
  647. OnFriendOnline(new FriendInfoEventArgs(friend));
  648. }
  649. }
  650. }
  651. }
  652. /// <summary>Process an incoming packet and raise the appropriate events</summary>
  653. /// <param name="sender">The sender</param>
  654. /// <param name="e">The EventArgs object containing the packet data</param>
  655. protected void OfflineNotificationHandler(object sender, PacketReceivedEventArgs e)
  656. {
  657. Packet packet = e.Packet;
  658. if (packet.Type == PacketType.OfflineNotification)
  659. {
  660. OfflineNotificationPacket notification = (OfflineNotificationPacket)packet;
  661. foreach (OfflineNotificationPacket.AgentBlockBlock block in notification.AgentBlock)
  662. {
  663. FriendInfo friend = new FriendInfo(block.AgentID, FriendRights.CanSeeOnline, FriendRights.CanSeeOnline);
  664. lock (FriendList.Dictionary)
  665. {
  666. if (!FriendList.Dictionary.ContainsKey(block.AgentID))
  667. FriendList.Dictionary[block.AgentID] = friend;
  668. friend = FriendList.Dictionary[block.AgentID];
  669. }
  670. friend.IsOnline = false;
  671. if (m_FriendOffline != null)
  672. {
  673. OnFriendOffline(new FriendInfoEventArgs(friend));
  674. }
  675. }
  676. }
  677. }
  678. /// <summary>Process an incoming packet and raise the appropriate events</summary>
  679. /// <param name="sender">The sender</param>
  680. /// <param name="e">The EventArgs object containing the packet data</param>
  681. private void ChangeUserRightsHandler(object sender, PacketReceivedEventArgs e)
  682. {
  683. Packet packet = e.Packet;
  684. if (packet.Type == PacketType.ChangeUserRights)
  685. {
  686. FriendInfo friend;
  687. ChangeUserRightsPacket rights = (ChangeUserRightsPacket)packet;
  688. foreach (ChangeUserRightsPacket.RightsBlock block in rights.Rights)
  689. {
  690. FriendRights newRights = (FriendRights)block.RelatedRights;
  691. if (FriendList.TryGetValue(block.AgentRelated, out friend))
  692. {
  693. friend.TheirFriendRights = newRights;
  694. if (m_FriendRights != null)
  695. {
  696. OnFriendRights(new FriendInfoEventArgs(friend));
  697. }
  698. }
  699. else if (block.AgentRelated == Client.Self.AgentID)
  700. {
  701. if (FriendList.TryGetValue(rights.AgentData.AgentID, out friend))
  702. {
  703. friend.MyFriendRights = newRights;
  704. if (m_FriendRights != null)
  705. {
  706. OnFriendRights(new FriendInfoEventArgs(friend));
  707. }
  708. }
  709. }
  710. }
  711. }
  712. }
  713. /// <summary>Process an incoming packet and raise the appropriate events</summary>
  714. /// <param name="sender">The sender</param>
  715. /// <param name="e">The EventArgs object containing the packet data</param>
  716. public void OnFindAgentReplyHandler(object sender, PacketReceivedEventArgs e)
  717. {
  718. if (m_FriendFound != null)
  719. {
  720. Packet packet = e.Packet;
  721. FindAgentPacket reply = (FindAgentPacket)packet;
  722. float x, y;
  723. UUID prey = reply.AgentBlock.Prey;
  724. ulong regionHandle = Helpers.GlobalPosToRegionHandle((float)reply.LocationBlock[0].GlobalX,
  725. (float)reply.LocationBlock[0].GlobalY, out x, out y);
  726. Vector3 xyz = new Vector3(x, y, 0f);
  727. OnFriendFoundReply(new FriendFoundReplyEventArgs(prey, regionHandle, xyz));
  728. }
  729. }
  730. #endregion
  731. private void Self_IM(object sender, InstantMessageEventArgs e)
  732. {
  733. if (e.IM.Dialog == InstantMessageDialog.FriendshipOffered)
  734. {
  735. if (m_FriendshipOffered != null)
  736. {
  737. if (FriendRequests.ContainsKey(e.IM.FromAgentID))
  738. FriendRequests[e.IM.FromAgentID] = e.IM.IMSessionID;
  739. else
  740. FriendRequests.Add(e.IM.FromAgentID, e.IM.IMSessionID);
  741. OnFriendshipOffered(new FriendshipOfferedEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, e.IM.IMSessionID));
  742. }
  743. }
  744. else if (e.IM.Dialog == InstantMessageDialog.FriendshipAccepted)
  745. {
  746. FriendInfo friend = new FriendInfo(e.IM.FromAgentID, FriendRights.CanSeeOnline,
  747. FriendRights.CanSeeOnline);
  748. friend.Name = e.IM.FromAgentName;
  749. lock (FriendList.Dictionary) FriendList[friend.UUID] = friend;
  750. if (m_FriendshipResponse != null)
  751. {
  752. OnFriendshipResponse(new FriendshipResponseEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, true));
  753. }
  754. RequestOnlineNotification(e.IM.FromAgentID);
  755. }
  756. else if (e.IM.Dialog == InstantMessageDialog.FriendshipDeclined)
  757. {
  758. if (m_FriendshipResponse != null)
  759. {
  760. OnFriendshipResponse(new FriendshipResponseEventArgs(e.IM.FromAgentID, e.IM.FromAgentName, false));
  761. }
  762. }
  763. }
  764. /// <summary>
  765. /// Populate FriendList <seealso cref="InternalDictionary"/> with data from the login reply
  766. /// </summary>
  767. /// <param name="loginSuccess">true if login was successful</param>
  768. /// <param name="redirect">true if login request is requiring a redirect</param>
  769. /// <param name="message">A string containing the response to the login request</param>
  770. /// <param name="reason">A string containing the reason for the request</param>
  771. /// <param name="replyData">A <seealso cref="LoginResponseData"/> object containing the decoded
  772. /// reply from the login server</param>
  773. private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason,
  774. LoginResponseData replyData)
  775. {
  776. int uuidLength = UUID.Zero.ToString().Length;
  777. if (loginSuccess && replyData.BuddyList != null)
  778. {
  779. foreach (BuddyListEntry buddy in replyData.BuddyList)
  780. {
  781. UUID bubid;
  782. string id = buddy.buddy_id.Length > uuidLength ? buddy.buddy_id.Substring(0, uuidLength) : buddy.buddy_id;
  783. if (UUID.TryParse(id, out bubid))
  784. {
  785. lock (FriendList.Dictionary)
  786. {
  787. if (!FriendList.ContainsKey(bubid))
  788. {
  789. FriendList[bubid] = new FriendInfo(bubid,
  790. (FriendRights)buddy.buddy_rights_given,
  791. (FriendRights)buddy.buddy_rights_has);
  792. }
  793. }
  794. }
  795. }
  796. }
  797. }
  798. }
  799. #region EventArgs
  800. /// <summary>Contains information on a member of our friends list</summary>
  801. public class FriendInfoEventArgs : EventArgs
  802. {
  803. private readonly FriendInfo m_Friend;
  804. /// <summary>Get the FriendInfo</summary>
  805. public FriendInfo Friend { get { return m_Friend; } }
  806. /// <summary>
  807. /// Construct a new instance of the FriendInfoEventArgs class
  808. /// </summary>
  809. /// <param name="friend">The FriendInfo</param>
  810. public FriendInfoEventArgs(FriendInfo friend)
  811. {
  812. this.m_Friend = friend;
  813. }
  814. }
  815. /// <summary>Contains Friend Names</summary>
  816. public class FriendNamesEventArgs : EventArgs
  817. {
  818. private readonly Dictionary<UUID, string> m_Names;
  819. /// <summary>A dictionary where the Key is the ID of the Agent,
  820. /// and the Value is a string containing their name</summary>
  821. public Dictionary<UUID, string> Names { get { return m_Names; } }
  822. /// <summary>
  823. /// Construct a new instance of the FriendNamesEventArgs class
  824. /// </summary>
  825. /// <param name="names">A dictionary where the Key is the ID of the Agent,
  826. /// and the Value is a string containing their name</param>
  827. public FriendNamesEventArgs(Dictionary<UUID, string> names)
  828. {
  829. this.m_Names = names;
  830. }
  831. }
  832. /// <summary>Sent when another agent requests a friendship with our agent</summary>
  833. public class FriendshipOfferedEventArgs : EventArgs
  834. {
  835. private readonly UUID m_AgentID;
  836. private readonly string m_AgentName;
  837. private readonly UUID m_SessionID;
  838. /// <summary>Get the ID of the agent requesting friendship</summary>
  839. public UUID AgentID { get { return m_AgentID; } }
  840. /// <summary>Get the name of the agent requesting friendship</summary>
  841. public string AgentName { get { return m_AgentName; } }
  842. /// <summary>Get the ID of the session, used in accepting or declining the
  843. /// friendship offer</summary>
  844. public UUID SessionID { get { return m_SessionID; } }
  845. /// <summary>
  846. /// Construct a new instance of the FriendshipOfferedEventArgs class
  847. /// </summary>
  848. /// <param name="agentID">The ID of the agent requesting friendship</param>
  849. /// <param name="agentName">The name of the agent requesting friendship</param>
  850. /// <param name="imSessionID">The ID of the session, used in accepting or declining the
  851. /// friendship offer</param>
  852. public FriendshipOfferedEventArgs(UUID agentID, string agentName, UUID imSessionID)
  853. {
  854. this.m_AgentID = agentID;
  855. this.m_AgentName = agentName;
  856. this.m_SessionID = imSessionID;
  857. }
  858. }
  859. /// <summary>A response containing the results of our request to form a friendship with another agent</summary>
  860. public class FriendshipResponseEventArgs : EventArgs
  861. {
  862. private readonly UUID m_AgentID;
  863. private readonly string m_AgentName;
  864. private readonly bool m_Accepted;
  865. /// <summary>Get the ID of the agent we requested a friendship with</summary>
  866. public UUID AgentID { get { return m_AgentID; } }
  867. /// <summary>Get the name of the agent we requested a friendship with</summary>
  868. public string AgentName { get { return m_AgentName; } }
  869. /// <summary>true if the agent accepted our friendship offer</summary>
  870. public bool Accepted { get { return m_Accepted; } }
  871. /// <summary>
  872. /// Construct a new instance of the FriendShipResponseEventArgs class
  873. /// </summary>
  874. /// <param name="agentID">The ID of the agent we requested a friendship with</param>
  875. /// <param name="agentName">The name of the agent we requested a friendship with</param>
  876. /// <param name="accepted">true if the agent accepted our friendship offer</param>
  877. public FriendshipResponseEventArgs(UUID agentID, string agentName, bool accepted)
  878. {
  879. this.m_AgentID = agentID;
  880. this.m_AgentName = agentName;
  881. this.m_Accepted = accepted;
  882. }
  883. }
  884. /// <summary>Contains data sent when a friend terminates a friendship with us</summary>
  885. public class FriendshipTerminatedEventArgs : EventArgs
  886. {
  887. private readonly UUID m_AgentID;
  888. private readonly string m_AgentName;
  889. /// <summary>Get the ID of the agent that terminated the friendship with us</summary>
  890. public UUID AgentID { get { return m_AgentID; } }
  891. /// <summary>Get the name of the agent that terminated the friendship with us</summary>
  892. public string AgentName { get { return m_AgentName; } }
  893. /// <summary>
  894. /// Construct a new instance of the FrindshipTerminatedEventArgs class
  895. /// </summary>
  896. /// <param name="agentID">The ID of the friend who terminated the friendship with us</param>
  897. /// <param name="agentName">The name of the friend who terminated the friendship with us</param>
  898. public FriendshipTerminatedEventArgs(UUID agentID, string agentName)
  899. {
  900. this.m_AgentID = agentID;
  901. this.m_AgentName = agentName;
  902. }
  903. }
  904. /// <summary>
  905. /// Data sent in response to a <see cref="FindFriend"/> request which contains the information to allow us to map the friends location
  906. /// </summary>
  907. public class FriendFoundReplyEventArgs : EventArgs
  908. {
  909. private readonly UUID m_AgentID;
  910. private readonly ulong m_RegionHandle;
  911. private readonly Vector3 m_Location;
  912. /// <summary>Get the ID of the agent we have received location information for</summary>
  913. public UUID AgentID { get { return m_AgentID; } }
  914. /// <summary>Get the region handle where our mapped friend is located</summary>
  915. public ulong RegionHandle { get { return m_RegionHandle; } }
  916. /// <summary>Get the simulator local position where our friend is located</summary>
  917. public Vector3 Location { get { return m_Location; } }
  918. /// <summary>
  919. /// Construct a new instance of the FriendFoundReplyEventArgs class
  920. /// </summary>
  921. /// <param name="agentID">The ID of the agent we have requested location information for</param>
  922. /// <param name="regionHandle">The region handle where our friend is located</param>
  923. /// <param name="location">The simulator local position our friend is located</param>
  924. public FriendFoundReplyEventArgs(UUID agentID, ulong regionHandle, Vector3 location)
  925. {
  926. this.m_AgentID = agentID;
  927. this.m_RegionHandle = regionHandle;
  928. this.m_Location = location;
  929. }
  930. }
  931. #endregion
  932. }