/* * Copyright (c) 2006-2016, openmetaverse.co * Copyright (c) 2021-2022, Sjofn LLC. * 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.co 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.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using OpenMetaverse.Http; using OpenMetaverse.Packets; using OpenMetaverse.Interfaces; using OpenMetaverse.Messages.Linden; using OpenMetaverse.StructuredData; namespace OpenMetaverse { #region Structs /// Information about agents display name public class AgentDisplayName { /// Agent UUID public UUID ID; /// Username public string UserName; /// Display name public string DisplayName; /// First name (legacy) public string LegacyFirstName; /// Last name (legacy) public string LegacyLastName; /// Full name (legacy) public string LegacyFullName => $"{LegacyFirstName} {LegacyLastName}"; /// Is display name default display name public bool IsDefaultDisplayName; /// Cache display name until public DateTime NextUpdate; /// Last updated timestamp public DateTime Updated; /// /// Creates AgentDisplayName object from OSD /// /// Incoming OSD data /// AgentDisplayName object public static AgentDisplayName FromOSD(OSD data) { AgentDisplayName ret = new AgentDisplayName(); OSDMap map = (OSDMap)data; ret.ID = map["id"]; ret.UserName = map["username"]; ret.DisplayName = map["display_name"]; ret.LegacyFirstName = map["legacy_first_name"]; ret.LegacyLastName = map["legacy_last_name"]; ret.IsDefaultDisplayName = map["is_display_name_default"]; ret.NextUpdate = map["display_name_next_update"]; ret.Updated = map["last_updated"]; return ret; } /// /// Return object as OSD map /// /// OSD containing agent's display name data public OSD GetOSD() { OSDMap map = new OSDMap { ["id"] = ID, ["username"] = UserName, ["display_name"] = DisplayName, ["legacy_first_name"] = LegacyFirstName, ["legacy_last_name"] = LegacyLastName, ["is_display_name_default"] = IsDefaultDisplayName, ["display_name_next_update"] = NextUpdate, ["last_updated"] = Updated }; return map; } public override string ToString() { return Helpers.StructToString(this); } } /// /// Holds group information for Avatars such as those you might find in a profile /// public struct AvatarGroup { /// true of Avatar accepts group notices public bool AcceptNotices; /// Groups Key public UUID GroupID; /// Texture Key for groups insignia public UUID GroupInsigniaID; /// Name of the group public string GroupName; /// Powers avatar has in the group public GroupPowers GroupPowers; /// Avatars Currently selected title public string GroupTitle; /// true of Avatar has chosen to list this in their profile public bool ListInProfile; } /// /// Contains an animation currently being played by an agent /// public struct Animation { /// The ID of the animation asset public UUID AnimationID; /// A number to indicate start order of currently playing animations /// On Linden Grids this number is unique per region, with OpenSim it is per client public int AnimationSequence; /// public UUID AnimationSourceObjectID; } /// /// Holds group information on an individual profile pick /// public struct ProfilePick { public UUID PickID; public UUID CreatorID; public bool TopPick; public UUID ParcelID; public string Name; public string Desc; public UUID SnapshotID; public string User; public string OriginalName; public string SimName; public Vector3d PosGlobal; public int SortOrder; public bool Enabled; } public struct ClassifiedAd { public UUID ClassifiedID; public uint Catagory; public UUID ParcelID; public uint ParentEstate; public UUID SnapShotID; public Vector3d Position; public byte ClassifiedFlags; public int Price; public string Name; public string Desc; } #endregion /// /// Retrieve friend status notifications, and retrieve avatar names and /// profiles /// public class AvatarManager { const int MAX_UUIDS_PER_PACKET = 100; #region Events /// The event subscribers, null if no subscribers private EventHandler m_AvatarAnimation; ///Raises the AvatarAnimation Event /// An AvatarAnimationEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarAnimation(AvatarAnimationEventArgs e) { EventHandler handler = m_AvatarAnimation; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarAnimationLock = new object(); /// Raised when the simulator sends us data containing /// an agents animation playlist public event EventHandler AvatarAnimation { add { lock (m_AvatarAnimationLock) { m_AvatarAnimation += value; } } remove { lock (m_AvatarAnimationLock) { m_AvatarAnimation -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarAppearance; ///Raises the AvatarAppearance Event /// A AvatarAppearanceEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarAppearance(AvatarAppearanceEventArgs e) { EventHandler handler = m_AvatarAppearance; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarAppearanceLock = new object(); /// Raised when the simulator sends us data containing /// the appearance information for an agent public event EventHandler AvatarAppearance { add { lock (m_AvatarAppearanceLock) { m_AvatarAppearance += value; } } remove { lock (m_AvatarAppearanceLock) { m_AvatarAppearance -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_UUIDNameReply; ///Raises the UUIDNameReply Event /// A UUIDNameReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnUUIDNameReply(UUIDNameReplyEventArgs e) { EventHandler handler = m_UUIDNameReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_UUIDNameReplyLock = new object(); /// Raised when the simulator sends us data containing /// agent names/id values public event EventHandler UUIDNameReply { add { lock (m_UUIDNameReplyLock) { m_UUIDNameReply += value; } } remove { lock (m_UUIDNameReplyLock) { m_UUIDNameReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarInterestsReply; ///Raises the AvatarInterestsReply Event /// A AvatarInterestsReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarInterestsReply(AvatarInterestsReplyEventArgs e) { EventHandler handler = m_AvatarInterestsReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarInterestsReplyLock = new object(); /// Raised when the simulator sends us data containing /// the interests listed in an agents profile public event EventHandler AvatarInterestsReply { add { lock (m_AvatarInterestsReplyLock) { m_AvatarInterestsReply += value; } } remove { lock (m_AvatarInterestsReplyLock) { m_AvatarInterestsReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarNotesReply; ///Raises the AvatarNotesReply Event /// A AvatarNotesReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarNotesReply(AvatarNotesReplyEventArgs e) { EventHandler handler = m_AvatarNotesReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarNotesReplyLock = new object(); /// Raised when the simulator sends us data containing /// the private notes listed in an agents profile public event EventHandler AvatarNotesReply { add { lock (m_AvatarNotesReplyLock) { m_AvatarNotesReply += value; } } remove { lock (m_AvatarNotesReplyLock) { m_AvatarNotesReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarPropertiesReply; ///Raises the AvatarPropertiesReply Event /// A AvatarPropertiesReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarPropertiesReply(AvatarPropertiesReplyEventArgs e) { EventHandler handler = m_AvatarPropertiesReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarPropertiesReplyLock = new object(); /// Raised when the simulator sends us data containing /// profile property information for an agent public event EventHandler AvatarPropertiesReply { add { lock (m_AvatarPropertiesReplyLock) { m_AvatarPropertiesReply += value; } } remove { lock (m_AvatarPropertiesReplyLock) { m_AvatarPropertiesReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarGroupsReply; ///Raises the AvatarGroupsReply Event /// A AvatarGroupsReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarGroupsReply(AvatarGroupsReplyEventArgs e) { EventHandler handler = m_AvatarGroupsReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarGroupsReplyLock = new object(); /// Raised when the simulator sends us data containing /// the group membership an agent is a member of public event EventHandler AvatarGroupsReply { add { lock (m_AvatarGroupsReplyLock) { m_AvatarGroupsReply += value; } } remove { lock (m_AvatarGroupsReplyLock) { m_AvatarGroupsReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarPickerReply; ///Raises the AvatarPickerReply Event /// A AvatarPickerReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarPickerReply(AvatarPickerReplyEventArgs e) { EventHandler handler = m_AvatarPickerReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarPickerReplyLock = new object(); /// Raised when the simulator sends us data containing /// name/id pair public event EventHandler AvatarPickerReply { add { lock (m_AvatarPickerReplyLock) { m_AvatarPickerReply += value; } } remove { lock (m_AvatarPickerReplyLock) { m_AvatarPickerReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_ViewerEffectPointAt; ///Raises the ViewerEffectPointAt Event /// A ViewerEffectPointAtEventArgs object containing /// the data sent from the simulator protected virtual void OnViewerEffectPointAt(ViewerEffectPointAtEventArgs e) { EventHandler handler = m_ViewerEffectPointAt; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_ViewerEffectPointAtLock = new object(); /// Raised when the simulator sends us data containing /// the objects and effect when an agent is pointing at public event EventHandler ViewerEffectPointAt { add { lock (m_ViewerEffectPointAtLock) { m_ViewerEffectPointAt += value; } } remove { lock (m_ViewerEffectPointAtLock) { m_ViewerEffectPointAt -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_ViewerEffectLookAt; ///Raises the ViewerEffectLookAt Event /// A ViewerEffectLookAtEventArgs object containing /// the data sent from the simulator protected virtual void OnViewerEffectLookAt(ViewerEffectLookAtEventArgs e) { EventHandler handler = m_ViewerEffectLookAt; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_ViewerEffectLookAtLock = new object(); /// Raised when the simulator sends us data containing /// the objects and effect when an agent is looking at public event EventHandler ViewerEffectLookAt { add { lock (m_ViewerEffectLookAtLock) { m_ViewerEffectLookAt += value; } } remove { lock (m_ViewerEffectLookAtLock) { m_ViewerEffectLookAt -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_ViewerEffect; ///Raises the ViewerEffect Event /// A ViewerEffectEventArgs object containing /// the data sent from the simulator protected virtual void OnViewerEffect(ViewerEffectEventArgs e) { EventHandler handler = m_ViewerEffect; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_ViewerEffectLock = new object(); /// Raised when the simulator sends us data containing /// an agents viewer effect information public event EventHandler ViewerEffect { add { lock (m_ViewerEffectLock) { m_ViewerEffect += value; } } remove { lock (m_ViewerEffectLock) { m_ViewerEffect -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarPicksReply; ///Raises the AvatarPicksReply Event /// A AvatarPicksReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarPicksReply(AvatarPicksReplyEventArgs e) { EventHandler handler = m_AvatarPicksReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarPicksReplyLock = new object(); /// Raised when the simulator sends us data containing /// the top picks from an agents profile public event EventHandler AvatarPicksReply { add { lock (m_AvatarPicksReplyLock) { m_AvatarPicksReply += value; } } remove { lock (m_AvatarPicksReplyLock) { m_AvatarPicksReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_PickInfoReply; ///Raises the PickInfoReply Event /// A PickInfoReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnPickInfoReply(PickInfoReplyEventArgs e) { EventHandler handler = m_PickInfoReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_PickInfoReplyLock = new object(); /// Raised when the simulator sends us data containing /// the Pick details public event EventHandler PickInfoReply { add { lock (m_PickInfoReplyLock) { m_PickInfoReply += value; } } remove { lock (m_PickInfoReplyLock) { m_PickInfoReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_AvatarClassifiedReply; ///Raises the AvatarClassifiedReply Event /// A AvatarClassifiedReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnAvatarClassifiedReply(AvatarClassifiedReplyEventArgs e) { EventHandler handler = m_AvatarClassifiedReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_AvatarClassifiedReplyLock = new object(); /// Raised when the simulator sends us data containing /// the classified ads an agent has placed public event EventHandler AvatarClassifiedReply { add { lock (m_AvatarClassifiedReplyLock) { m_AvatarClassifiedReply += value; } } remove { lock (m_AvatarClassifiedReplyLock) { m_AvatarClassifiedReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_ClassifiedInfoReply; ///Raises the ClassifiedInfoReply Event /// A ClassifiedInfoReplyEventArgs object containing /// the data sent from the simulator protected virtual void OnClassifiedInfoReply(ClassifiedInfoReplyEventArgs e) { EventHandler handler = m_ClassifiedInfoReply; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_ClassifiedInfoReplyLock = new object(); /// Raised when the simulator sends us data containing /// the details of a classified ad public event EventHandler ClassifiedInfoReply { add { lock (m_ClassifiedInfoReplyLock) { m_ClassifiedInfoReply += value; } } remove { lock (m_ClassifiedInfoReplyLock) { m_ClassifiedInfoReply -= value; } } } /// The event subscribers, null if no subscribers private EventHandler m_DisplayNameUpdate; ///Raises the DisplayNameUpdate Event /// A DisplayNameUpdateEventArgs object containing /// the data sent from the simulator protected virtual void OnDisplayNameUpdate(DisplayNameUpdateEventArgs e) { EventHandler handler = m_DisplayNameUpdate; handler?.Invoke(this, e); } /// Thread sync lock object private readonly object m_DisplayNameUpdateLock = new object(); /// Raised when the simulator sends us data containing /// the details of display name change public event EventHandler DisplayNameUpdate { add { lock (m_DisplayNameUpdateLock) { m_DisplayNameUpdate += value; } } remove { lock (m_DisplayNameUpdateLock) { m_DisplayNameUpdate -= value; } } } #endregion Events #region Delegates /// /// Callback giving results when fetching display names /// /// If the request was successful /// Array of display names /// Array of UUIDs that could not be fetched public delegate void DisplayNamesCallback(bool success, AgentDisplayName[] names, UUID[] badIDs); #endregion Delegates private GridClient Client; /// /// Represents other avatars /// /// public AvatarManager(GridClient client) { Client = client; // Avatar appearance callback Client.Network.RegisterCallback(PacketType.AvatarAppearance, AvatarAppearanceHandler); // Avatar profile callbacks Client.Network.RegisterCallback(PacketType.AvatarPropertiesReply, AvatarPropertiesHandler); // Client.Network.RegisterCallback(PacketType.AvatarStatisticsReply, AvatarStatisticsHandler); Client.Network.RegisterCallback(PacketType.AvatarInterestsReply, AvatarInterestsHandler); Client.Network.RegisterCallback(PacketType.AvatarNotesReply, AvatarNotesHandler); // Avatar group callback Client.Network.RegisterCallback(PacketType.AvatarGroupsReply, AvatarGroupsReplyHandler); Client.Network.RegisterEventCallback("AgentGroupDataUpdate", AvatarGroupsReplyMessageHandler); Client.Network.RegisterEventCallback("AvatarGroupsReply", AvatarGroupsReplyMessageHandler); // Viewer effect callback Client.Network.RegisterCallback(PacketType.ViewerEffect, ViewerEffectHandler); // Other callbacks Client.Network.RegisterCallback(PacketType.UUIDNameReply, UUIDNameReplyHandler); Client.Network.RegisterCallback(PacketType.AvatarPickerReply, AvatarPickerReplyHandler); Client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler); // Picks callbacks Client.Network.RegisterCallback(PacketType.AvatarPicksReply, AvatarPicksReplyHandler); Client.Network.RegisterCallback(PacketType.PickInfoReply, PickInfoReplyHandler); // Classifieds callbacks Client.Network.RegisterCallback(PacketType.AvatarClassifiedReply, AvatarClassifiedReplyHandler); Client.Network.RegisterCallback(PacketType.ClassifiedInfoReply, ClassifiedInfoReplyHandler); Client.Network.RegisterEventCallback("DisplayNameUpdate", DisplayNameUpdateMessageHandler); } /// Tracks the specified avatar on your map /// Avatar ID to track public void RequestTrackAgent(UUID preyID) { TrackAgentPacket p = new TrackAgentPacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID }, TargetData = {PreyID = preyID} }; Client.Network.SendPacket(p); } /// /// Request a single avatar name /// /// The avatar key to retrieve a name for public void RequestAvatarName(UUID id) { UUIDNameRequestPacket request = new UUIDNameRequestPacket {UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[1]}; request.UUIDNameBlock[0] = new UUIDNameRequestPacket.UUIDNameBlockBlock {ID = id}; Client.Network.SendPacket(request); } /// /// Request a list of avatar names /// /// The avatar keys to retrieve names for public void RequestAvatarNames(List ids) { int m = MAX_UUIDS_PER_PACKET; int n = ids.Count / m; // Number of full requests to make int i = 0; UUIDNameRequestPacket request; for (int j = 0; j < n; j++) { request = new UUIDNameRequestPacket {UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[m]}; for (; i < (j + 1) * m; i++) { request.UUIDNameBlock[i % m] = new UUIDNameRequestPacket.UUIDNameBlockBlock {ID = ids[i]}; } Client.Network.SendPacket(request); } // Get any remaining names after left after the full requests if (ids.Count > n * m) { request = new UUIDNameRequestPacket { UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[ids.Count - n * m] }; for (; i < ids.Count; i++) { request.UUIDNameBlock[i % m] = new UUIDNameRequestPacket.UUIDNameBlockBlock {ID = ids[i]}; } Client.Network.SendPacket(request); } } /// /// Check if Display Names functionality is available /// /// True if Display name functionality is available public bool DisplayNamesAvailable() { return Client.Network.CurrentSim?.Caps?.CapabilityURI("GetDisplayNames") != null; } /// /// Request retrieval of display names (max 90 names per request) /// /// List of UUIDs to lookup /// Callback to report result of the operation public void GetDisplayNames(List ids, DisplayNamesCallback callback) { if (!DisplayNamesAvailable() || ids.Count == 0) { callback(false, null, null); } StringBuilder query = new StringBuilder(); for (int i = 0; i < ids.Count && i < 90; i++) { query.AppendFormat("ids={0}", ids[i]); if (i < ids.Count - 1) { query.Append("&"); } } Uri uri = new Uri(Client.Network.CurrentSim.Caps.CapabilityURI("GetDisplayNames").AbsoluteUri + "/?" + query); Task req = Client.HttpCapsClient.GetRequestAsync(uri, CancellationToken.None, (response, data, error) => { try { if (error != null) { throw error; } GetDisplayNamesMessage msg = new GetDisplayNamesMessage(); OSD result = OSDParser.Deserialize(data); if (result is OSDMap respMap) { msg.Deserialize(respMap); callback(true, msg.Agents, msg.BadIDs); } } catch (Exception ex) { Logger.Log("Failed to call GetDisplayNames capability: ", Helpers.LogLevel.Warning, Client, ex); callback(false, null, null); } }); } /// /// Start a request for Avatar Properties /// /// public void RequestAvatarProperties(UUID avatarid) { AvatarPropertiesRequestPacket aprp = new AvatarPropertiesRequestPacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, AvatarID = avatarid } }; Client.Network.SendPacket(aprp); } /// /// Search for an avatar (first name, last name) /// /// The name to search for /// An ID to associate with this query public void RequestAvatarNameSearch(string name, UUID queryID) { AvatarPickerRequestPacket aprp = new AvatarPickerRequestPacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, QueryID = queryID }, Data = {Name = Utils.StringToBytes(name)} }; Client.Network.SendPacket(aprp); } /// /// Request avatar notes from simulator /// /// Target agent UUID public void RequestAvatarNotes(UUID avatarid) { GenericMessagePacket gmp = new GenericMessagePacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, TransactionID = UUID.Zero }, MethodData = { Method = Utils.StringToBytes("avatarnotesrequest"), Invoice = UUID.Zero }, ParamList = new GenericMessagePacket.ParamListBlock[1] }; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock { Parameter = Utils.StringToBytes(avatarid.ToString()) }; Client.Network.SendPacket(gmp); } /// /// Start a request for Avatar Picks /// /// UUID of the avatar public void RequestAvatarPicks(UUID avatarid) { GenericMessagePacket gmp = new GenericMessagePacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, TransactionID = UUID.Zero }, MethodData = { Method = Utils.StringToBytes("avatarpicksrequest"), Invoice = UUID.Zero }, ParamList = new GenericMessagePacket.ParamListBlock[1] }; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(avatarid.ToString())}; Client.Network.SendPacket(gmp); } /// /// Start a request for Avatar Classifieds /// /// UUID of the avatar public void RequestAvatarClassified(UUID avatarid) { GenericMessagePacket gmp = new GenericMessagePacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, TransactionID = UUID.Zero }, MethodData = { Method = Utils.StringToBytes("avatarclassifiedsrequest"), Invoice = UUID.Zero }, ParamList = new GenericMessagePacket.ParamListBlock[1] }; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(avatarid.ToString())}; Client.Network.SendPacket(gmp); } /// /// Start a request for details of a specific profile pick /// /// UUID of the avatar /// UUID of the profile pick public void RequestPickInfo(UUID avatarid, UUID pickid) { GenericMessagePacket gmp = new GenericMessagePacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, TransactionID = UUID.Zero }, MethodData = { Method = Utils.StringToBytes("pickinforequest"), Invoice = UUID.Zero }, ParamList = new GenericMessagePacket.ParamListBlock[2] }; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(avatarid.ToString())}; gmp.ParamList[1] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(pickid.ToString())}; Client.Network.SendPacket(gmp); } /// /// Start a request for details of a specific profile classified /// /// UUID of the avatar /// UUID of the profile classified public void RequestClassifiedInfo(UUID avatarid, UUID classifiedid) { GenericMessagePacket gmp = new GenericMessagePacket { AgentData = { AgentID = Client.Self.AgentID, SessionID = Client.Self.SessionID, TransactionID = UUID.Zero }, MethodData = { Method = Utils.StringToBytes("classifiedinforequest"), Invoice = UUID.Zero }, ParamList = new GenericMessagePacket.ParamListBlock[2] }; gmp.ParamList[0] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(avatarid.ToString())}; gmp.ParamList[1] = new GenericMessagePacket.ParamListBlock {Parameter = Utils.StringToBytes(classifiedid.ToString())}; Client.Network.SendPacket(gmp); } #region Packet Handlers /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void UUIDNameReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_UUIDNameReply != null) { Packet packet = e.Packet; var names = new Dictionary(); UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet; foreach (var block in reply.UUIDNameBlock) { names[block.ID] = Utils.BytesToString(block.FirstName) + " " + Utils.BytesToString(block.LastName); } OnUUIDNameReply(new UUIDNameReplyEventArgs(names)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarAnimationHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; AvatarAnimationPacket data = (AvatarAnimationPacket)packet; List signaledAnimations = new List(data.AnimationList.Length); for (int i = 0; i < data.AnimationList.Length; i++) { Animation animation = new Animation { AnimationID = data.AnimationList[i].AnimID, AnimationSequence = data.AnimationList[i].AnimSequenceID }; if (i < data.AnimationSourceList.Length) { animation.AnimationSourceObjectID = data.AnimationSourceList[i].ObjectID; } signaledAnimations.Add(animation); } Avatar avatar = e.Simulator.ObjectsAvatars.Find(avi => avi.ID == data.Sender.ID); if (avatar != null) { avatar.Animations = signaledAnimations; } OnAvatarAnimation(new AvatarAnimationEventArgs(data.Sender.ID, signaledAnimations)); } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarAppearanceHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarAppearance != null || Client.Settings.AVATAR_TRACKING) { Packet packet = e.Packet; Simulator simulator = e.Simulator; AvatarAppearancePacket appearance = (AvatarAppearancePacket)packet; var visualParams = appearance.VisualParam.Select(block => block.ParamValue).ToList(); var textureEntry = new Primitive.TextureEntry(appearance.ObjectData.TextureEntry, 0, appearance.ObjectData.TextureEntry.Length); var defaultTexture = textureEntry.DefaultTexture; var faceTextures = textureEntry.FaceTextures; byte appearanceVersion = 0; int COFVersion = 0; AppearanceFlags appearanceFlags = 0; if (appearance.AppearanceData != null && appearance.AppearanceData.Length > 0) { appearanceVersion = appearance.AppearanceData[0].AppearanceVersion; COFVersion = appearance.AppearanceData[0].CofVersion; appearanceFlags = (AppearanceFlags)appearance.AppearanceData[0].Flags; } Avatar av = simulator.ObjectsAvatars.Find((Avatar a) => a.ID == appearance.Sender.ID); if (av != null) { av.Textures = textureEntry; av.VisualParameters = visualParams.ToArray(); av.AppearanceVersion = appearanceVersion; av.COFVersion = COFVersion; av.AppearanceFlags = appearanceFlags; } OnAvatarAppearance(new AvatarAppearanceEventArgs(simulator, appearance.Sender.ID, appearance.Sender.IsTrial, defaultTexture, faceTextures, visualParams, appearanceVersion, COFVersion, appearanceFlags)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarPropertiesHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarPropertiesReply != null) { Packet packet = e.Packet; AvatarPropertiesReplyPacket reply = (AvatarPropertiesReplyPacket)packet; Avatar.AvatarProperties properties = new Avatar.AvatarProperties { ProfileImage = reply.PropertiesData.ImageID, FirstLifeImage = reply.PropertiesData.FLImageID, Partner = reply.PropertiesData.PartnerID, AboutText = Utils.BytesToString(reply.PropertiesData.AboutText), FirstLifeText = Utils.BytesToString(reply.PropertiesData.FLAboutText), BornOn = Utils.BytesToString(reply.PropertiesData.BornOn) }; if (reply.PropertiesData.CharterMember.Length == 1) { uint charter = Utils.BytesToUInt(reply.PropertiesData.CharterMember); if (charter == 0) { properties.CharterMember = "Resident"; } else if (charter == 1) { properties.CharterMember = "Trial"; } else if (charter == 2) { properties.CharterMember = "Charter"; } else if (charter == 3) { properties.CharterMember = "Employee"; } } else if (reply.PropertiesData.CharterMember.Length > 1) { properties.CharterMember = Utils.BytesToString(reply.PropertiesData.CharterMember); } properties.Flags = (ProfileFlags)reply.PropertiesData.Flags; properties.ProfileURL = Utils.BytesToString(reply.PropertiesData.ProfileURL); OnAvatarPropertiesReply(new AvatarPropertiesReplyEventArgs(reply.AgentData.AvatarID, properties)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarInterestsHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarInterestsReply != null) { Packet packet = e.Packet; AvatarInterestsReplyPacket airp = (AvatarInterestsReplyPacket)packet; Avatar.Interests interests = new Avatar.Interests { WantToMask = airp.PropertiesData.WantToMask, WantToText = Utils.BytesToString(airp.PropertiesData.WantToText), SkillsMask = airp.PropertiesData.SkillsMask, SkillsText = Utils.BytesToString(airp.PropertiesData.SkillsText), LanguagesText = Utils.BytesToString(airp.PropertiesData.LanguagesText) }; OnAvatarInterestsReply(new AvatarInterestsReplyEventArgs(airp.AgentData.AvatarID, interests)); } } protected void AvatarNotesHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarNotesReply != null) { Packet packet = e.Packet; AvatarNotesReplyPacket anrp = (AvatarNotesReplyPacket)packet; string notes = Utils.BytesToString(anrp.Data.Notes); OnAvatarNotesReply(new AvatarNotesReplyEventArgs(anrp.Data.TargetID, notes)); } } /// /// EQ Message fired when someone nearby changes their display name /// /// The message key /// the IMessage object containing the deserialized data sent from the simulator /// The which originated the packet protected void DisplayNameUpdateMessageHandler(string capsKey, IMessage message, Simulator simulator) { if (m_DisplayNameUpdate != null) { DisplayNameUpdateMessage msg = (DisplayNameUpdateMessage)message; OnDisplayNameUpdate(new DisplayNameUpdateEventArgs(msg.OldDisplayName, msg.DisplayName)); } } /// /// Crossed region handler for message that comes across the EventQueue. Sent to an agent /// when the agent crosses a sim border into a new region. /// /// The message key /// the IMessage object containing the deserialized data sent from the simulator /// The which originated the packet protected void AvatarGroupsReplyMessageHandler(string capsKey, IMessage message, Simulator simulator) { AgentGroupDataUpdateMessage msg = (AgentGroupDataUpdateMessage)message; List avatarGroups = new List(msg.GroupDataBlock.Length); for (int i = 0; i < msg.GroupDataBlock.Length; i++) { AvatarGroup avatarGroup = new AvatarGroup { AcceptNotices = msg.GroupDataBlock[i].AcceptNotices, GroupID = msg.GroupDataBlock[i].GroupID, GroupInsigniaID = msg.GroupDataBlock[i].GroupInsigniaID, GroupName = msg.GroupDataBlock[i].GroupName, GroupPowers = msg.GroupDataBlock[i].GroupPowers, ListInProfile = msg.NewGroupDataBlock[i].ListInProfile }; avatarGroups.Add(avatarGroup); } OnAvatarGroupsReply(new AvatarGroupsReplyEventArgs(msg.AgentID, avatarGroups)); } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarGroupsReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarGroupsReply != null) { Packet packet = e.Packet; AvatarGroupsReplyPacket groups = (AvatarGroupsReplyPacket)packet; List avatarGroups = new List(groups.GroupData.Length); foreach (AvatarGroupsReplyPacket.GroupDataBlock groupData in groups.GroupData) { AvatarGroup avatarGroup = new AvatarGroup { AcceptNotices = groupData.AcceptNotices, GroupID = groupData.GroupID, GroupInsigniaID = groupData.GroupInsigniaID, GroupName = Utils.BytesToString(groupData.GroupName), GroupPowers = (GroupPowers) groupData.GroupPowers, GroupTitle = Utils.BytesToString(groupData.GroupTitle), ListInProfile = groups.NewGroupData.ListInProfile }; avatarGroups.Add(avatarGroup); } OnAvatarGroupsReply(new AvatarGroupsReplyEventArgs(groups.AgentData.AvatarID, avatarGroups)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarPickerReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarPickerReply != null) { Packet packet = e.Packet; AvatarPickerReplyPacket reply = (AvatarPickerReplyPacket)packet; Dictionary avatars = new Dictionary(); foreach (AvatarPickerReplyPacket.DataBlock block in reply.Data) { avatars[block.AvatarID] = Utils.BytesToString(block.FirstName) + " " + Utils.BytesToString(block.LastName); } OnAvatarPickerReply(new AvatarPickerReplyEventArgs(reply.AgentData.QueryID, avatars)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void ViewerEffectHandler(object sender, PacketReceivedEventArgs e) { Packet packet = e.Packet; ViewerEffectPacket effect = (ViewerEffectPacket)packet; foreach (ViewerEffectPacket.EffectBlock block in effect.Effect) { EffectType type = (EffectType)block.Type; // Each ViewerEffect type uses it's own custom binary format for additional data. Fun eh? switch (type) { case EffectType.Text: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.Icon: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.Connector: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.FlexibleObject: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.AnimalControls: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.AnimationObject: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.Cloth: Logger.Log("Received a ViewerEffect of type " + type + ", implement me!", Helpers.LogLevel.Warning, Client); break; case EffectType.Glow: Logger.Log("Received a Glow ViewerEffect which is not implemented yet", Helpers.LogLevel.Warning, Client); break; case EffectType.Beam: case EffectType.Point: case EffectType.Trail: case EffectType.Sphere: case EffectType.Spiral: case EffectType.Edit: if (m_ViewerEffect != null) { if (block.TypeData.Length == 56) { UUID sourceAvatar = new UUID(block.TypeData, 0); UUID targetObject = new UUID(block.TypeData, 16); Vector3d targetPos = new Vector3d(block.TypeData, 32); OnViewerEffect(new ViewerEffectEventArgs(type, sourceAvatar, targetObject, targetPos, block.Duration, block.ID)); } else { Logger.Log("Received a " + type + " ViewerEffect with an incorrect TypeData size of " + block.TypeData.Length + " bytes", Helpers.LogLevel.Warning, Client); } } break; case EffectType.LookAt: if (m_ViewerEffectLookAt != null) { if (block.TypeData.Length == 57) { UUID sourceAvatar = new UUID(block.TypeData, 0); UUID targetObject = new UUID(block.TypeData, 16); Vector3d targetPos = new Vector3d(block.TypeData, 32); LookAtType lookAt = (LookAtType)block.TypeData[56]; OnViewerEffectLookAt(new ViewerEffectLookAtEventArgs(sourceAvatar, targetObject, targetPos, lookAt, block.Duration, block.ID)); } else { Logger.Log("Received a LookAt ViewerEffect with an incorrect TypeData size of " + block.TypeData.Length + " bytes", Helpers.LogLevel.Warning, Client); } } break; case EffectType.PointAt: if (m_ViewerEffectPointAt != null) { if (block.TypeData.Length == 57) { UUID sourceAvatar = new UUID(block.TypeData, 0); UUID targetObject = new UUID(block.TypeData, 16); Vector3d targetPos = new Vector3d(block.TypeData, 32); PointAtType pointAt = (PointAtType)block.TypeData[56]; OnViewerEffectPointAt(new ViewerEffectPointAtEventArgs(e.Simulator, sourceAvatar, targetObject, targetPos, pointAt, block.Duration, block.ID)); } else { Logger.Log("Received a PointAt ViewerEffect with an incorrect TypeData size of " + block.TypeData.Length + " bytes", Helpers.LogLevel.Warning, Client); } } break; default: Logger.Log("Received a ViewerEffect with an unknown type " + type, Helpers.LogLevel.Warning, Client); break; } } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarPicksReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarPicksReply == null) { return; } Packet packet = e.Packet; AvatarPicksReplyPacket p = (AvatarPicksReplyPacket)packet; var picks = p.Data.ToDictionary(b => b.PickID, b => Utils.BytesToString(b.PickName)); OnAvatarPicksReply(new AvatarPicksReplyEventArgs(p.AgentData.TargetID, picks)); } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void PickInfoReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_PickInfoReply != null) { Packet packet = e.Packet; PickInfoReplyPacket p = (PickInfoReplyPacket)packet; ProfilePick ret = new ProfilePick { CreatorID = p.Data.CreatorID, Desc = Utils.BytesToString(p.Data.Desc), Enabled = p.Data.Enabled, Name = Utils.BytesToString(p.Data.Name), OriginalName = Utils.BytesToString(p.Data.OriginalName), ParcelID = p.Data.ParcelID, PickID = p.Data.PickID, PosGlobal = p.Data.PosGlobal, SimName = Utils.BytesToString(p.Data.SimName), SnapshotID = p.Data.SnapshotID, SortOrder = p.Data.SortOrder, TopPick = p.Data.TopPick, User = Utils.BytesToString(p.Data.User) }; OnPickInfoReply(new PickInfoReplyEventArgs(ret.PickID, ret)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void AvatarClassifiedReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarClassifiedReply != null) { Packet packet = e.Packet; AvatarClassifiedReplyPacket p = (AvatarClassifiedReplyPacket)packet; var classifieds = p.Data.ToDictionary(b => b.ClassifiedID, b => Utils.BytesToString(b.Name)); OnAvatarClassifiedReply(new AvatarClassifiedReplyEventArgs(p.AgentData.TargetID, classifieds)); } } /// Process an incoming packet and raise the appropriate events /// The sender /// The EventArgs object containing the packet data protected void ClassifiedInfoReplyHandler(object sender, PacketReceivedEventArgs e) { if (m_AvatarClassifiedReply != null) { Packet packet = e.Packet; ClassifiedInfoReplyPacket p = (ClassifiedInfoReplyPacket)packet; ClassifiedAd ret = new ClassifiedAd { Desc = Utils.BytesToString(p.Data.Desc), Name = Utils.BytesToString(p.Data.Name), ParcelID = p.Data.ParcelID, ClassifiedID = p.Data.ClassifiedID, Position = p.Data.PosGlobal, SnapShotID = p.Data.SnapshotID, Price = p.Data.PriceForListing, ParentEstate = p.Data.ParentEstate, ClassifiedFlags = p.Data.ClassifiedFlags, Catagory = p.Data.Category }; OnClassifiedInfoReply(new ClassifiedInfoReplyEventArgs(ret.ClassifiedID, ret)); } } #endregion Packet Handlers } #region EventArgs /// Provides data for the event /// The event occurs when the simulator sends /// the animation playlist for an agent /// /// The following code example uses the and /// properties to display the animation playlist of an avatar on the window. /// /// // subscribe to the event /// Client.Avatars.AvatarAnimation += Avatars_AvatarAnimation; /// /// private void Avatars_AvatarAnimation(object sender, AvatarAnimationEventArgs e) /// { /// // create a dictionary of "known" animations from the Animations class using System.Reflection /// Dictionary<UUID, string> systemAnimations = new Dictionary<UUID, string>(); /// Type type = typeof(Animations); /// System.Reflection.FieldInfo[] fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); /// foreach (System.Reflection.FieldInfo field in fields) /// { /// systemAnimations.Add((UUID)field.GetValue(type), field.Name); /// } /// /// // find out which animations being played are known animations and which are assets /// foreach (Animation animation in e.Animations) /// { /// if (systemAnimations.ContainsKey(animation.AnimationID)) /// { /// Console.WriteLine("{0} is playing {1} ({2}) sequence {3}", e.AvatarID, /// systemAnimations[animation.AnimationID], animation.AnimationSequence); /// } /// else /// { /// Console.WriteLine("{0} is playing {1} (Asset) sequence {2}", e.AvatarID, /// animation.AnimationID, animation.AnimationSequence); /// } /// } /// } /// /// public class AvatarAnimationEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } /// Get the list of animations to start public List Animations { get; } /// /// Construct a new instance of the AvatarAnimationEventArgs class /// /// The ID of the agent /// The list of animations to start public AvatarAnimationEventArgs(UUID avatarID, List anims) { this.AvatarID = avatarID; this.Animations = anims; } } /// Provides data for the event /// The event occurs when the simulator sends /// the appearance data for an avatar /// /// The following code example uses the and /// properties to display the selected shape of an avatar on the window. /// /// // subscribe to the event /// Client.Avatars.AvatarAppearance += Avatars_AvatarAppearance; /// /// // handle the data when the event is raised /// void Avatars_AvatarAppearance(object sender, AvatarAppearanceEventArgs e) /// { /// Console.WriteLine("The Agent {0} is using a {1} shape.", e.AvatarID, (e.VisualParams[31] > 0) : "male" ? "female") /// } /// /// public class AvatarAppearanceEventArgs : EventArgs { /// Get the Simulator this request is from of the agent public Simulator Simulator { get; } /// Get the ID of the agent public UUID AvatarID { get; } /// true if the agent is a trial account public bool IsTrial { get; } /// Get the default agent texture public Primitive.TextureEntryFace DefaultTexture { get; } /// Get the agents appearance layer textures public Primitive.TextureEntryFace[] FaceTextures { get; } /// Get the for the agent public List VisualParams { get; } /// Version of the appearance system used. /// Value greater than 0 indicates that server side baking is used public byte AppearanceVersion { get; } /// Version of the Current Outfit Folder the appearance is based on public int COFVersion { get; } /// Appearance flags, introduced with server side baking, currently unused public AppearanceFlags AppearanceFlags { get; } /// /// Construct a new instance of the AvatarAppearanceEventArgs class /// /// The simulator request was from /// The ID of the agent /// true of the agent is a trial account /// The default agent texture /// The agents appearance layer textures /// The for the agent /// Appearance Version /// Current outfit folder version /// Appearance Flags public AvatarAppearanceEventArgs(Simulator sim, UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture, Primitive.TextureEntryFace[] faceTextures, List visualParams, byte appearanceVersion, int COFVersion, AppearanceFlags appearanceFlags) { this.Simulator = sim; this.AvatarID = avatarID; this.IsTrial = isTrial; this.DefaultTexture = defaultTexture; this.FaceTextures = faceTextures; this.VisualParams = visualParams; this.AppearanceVersion = appearanceVersion; this.COFVersion = COFVersion; this.AppearanceFlags = appearanceFlags; } } /// Represents the interests from the profile of an agent public class AvatarInterestsReplyEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } /// Get the interests of the agent public Avatar.Interests Interests { get; } public AvatarInterestsReplyEventArgs(UUID avatarID, Avatar.Interests interests) { this.AvatarID = avatarID; this.Interests = interests; } } /// Represents the private notes from the profile of an agent public class AvatarNotesReplyEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } /// Get the interests of the agent public string Notes { get; } public AvatarNotesReplyEventArgs(UUID avatarID, string notes) { this.AvatarID = avatarID; this.Notes = notes; } } /// The properties of an agent public class AvatarPropertiesReplyEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } public Avatar.AvatarProperties Properties { get; } public AvatarPropertiesReplyEventArgs(UUID avatarID, Avatar.AvatarProperties properties) { this.AvatarID = avatarID; this.Properties = properties; } } public class AvatarGroupsReplyEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } public List Groups { get; } public AvatarGroupsReplyEventArgs(UUID avatarID, List avatarGroups) { this.AvatarID = avatarID; this.Groups = avatarGroups; } } public class AvatarPicksReplyEventArgs : EventArgs { /// Get the ID of the agent public UUID AvatarID { get; } public Dictionary Picks { get; } public AvatarPicksReplyEventArgs(UUID avatarid, Dictionary picks) { this.AvatarID = avatarid; this.Picks = picks; } } public class PickInfoReplyEventArgs : EventArgs { public UUID PickID { get; } public ProfilePick Pick { get; } public PickInfoReplyEventArgs(UUID pickid, ProfilePick pick) { this.PickID = pickid; this.Pick = pick; } } public class AvatarClassifiedReplyEventArgs : EventArgs { /// Get the ID of the avatar public UUID AvatarID { get; } public Dictionary Classifieds { get; } public AvatarClassifiedReplyEventArgs(UUID avatarid, Dictionary classifieds) { this.AvatarID = avatarid; this.Classifieds = classifieds; } } public class ClassifiedInfoReplyEventArgs : EventArgs { public UUID ClassifiedID { get; } public ClassifiedAd Classified { get; } public ClassifiedInfoReplyEventArgs(UUID classifiedID, ClassifiedAd Classified) { this.ClassifiedID = classifiedID; this.Classified = Classified; } } public class UUIDNameReplyEventArgs : EventArgs { public Dictionary Names { get; } public UUIDNameReplyEventArgs(Dictionary names) { this.Names = names; } } public class AvatarPickerReplyEventArgs : EventArgs { public UUID QueryID { get; } public Dictionary Avatars { get; } public AvatarPickerReplyEventArgs(UUID queryID, Dictionary avatars) { this.QueryID = queryID; this.Avatars = avatars; } } public class ViewerEffectEventArgs : EventArgs { public EffectType Type { get; } public UUID SourceID { get; } public UUID TargetID { get; } public Vector3d TargetPosition { get; } public float Duration { get; } public UUID EffectID { get; } public ViewerEffectEventArgs(EffectType type, UUID sourceID, UUID targetID, Vector3d targetPos, float duration, UUID id) { this.Type = type; this.SourceID = sourceID; this.TargetID = targetID; this.TargetPosition = targetPos; this.Duration = duration; this.EffectID = id; } } public class ViewerEffectPointAtEventArgs : EventArgs { public Simulator Simulator { get; } public UUID SourceID { get; } public UUID TargetID { get; } public Vector3d TargetPosition { get; } public PointAtType PointType { get; } public float Duration { get; } public UUID EffectID { get; } public ViewerEffectPointAtEventArgs(Simulator simulator, UUID sourceID, UUID targetID, Vector3d targetPos, PointAtType pointType, float duration, UUID id) { this.Simulator = simulator; this.SourceID = sourceID; this.TargetID = targetID; this.TargetPosition = targetPos; this.PointType = pointType; this.Duration = duration; this.EffectID = id; } } public class ViewerEffectLookAtEventArgs : EventArgs { public UUID SourceID { get; } public UUID TargetID { get; } public Vector3d TargetPosition { get; } public LookAtType LookType { get; } public float Duration { get; } public UUID EffectID { get; } public ViewerEffectLookAtEventArgs(UUID sourceID, UUID targetID, Vector3d targetPos, LookAtType lookType, float duration, UUID id) { this.SourceID = sourceID; this.TargetID = targetID; this.TargetPosition = targetPos; this.LookType = lookType; this.Duration = duration; this.EffectID = id; } } /// /// Event args class for display name notification messages /// public class DisplayNameUpdateEventArgs : EventArgs { public string OldDisplayName { get; } public AgentDisplayName DisplayName { get; } public DisplayNameUpdateEventArgs(string oldDisplayName, AgentDisplayName displayName) { this.OldDisplayName = oldDisplayName; this.DisplayName = displayName; } } #endregion }