corrade-vassal – Blame information for rev 1

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