diff --git a/libsecondlife-cs/MainAvatar.cs b/libsecondlife-cs/MainAvatar.cs index df13d8ee..c39ad9d4 100644 --- a/libsecondlife-cs/MainAvatar.cs +++ b/libsecondlife-cs/MainAvatar.cs @@ -1,1685 +1,1703 @@ -/* - * Copyright (c) 2006, Second Life Reverse Engineering Team - * 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 Second Life Reverse Engineering Team 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.Timers; -using System.Net; -using System.Collections; -using System.Collections.Generic; -using System.Text; -using libsecondlife.Packets; - -namespace libsecondlife -{ - /// - /// Class to hold Client Avatar's data - /// - public partial class MainAvatar - { - /// - /// Current teleport status - /// - public enum TeleportStatus - { - /// - None, - /// Teleport Start - Start, - /// Teleport in Progress - Progress, - /// Teleport Failed - Failed, - /// Teleport Completed - Finished - } - - /// - /// Special commands used in Instant Messages - /// - public enum InstantMessageDialog : byte - { - /// Indicates a regular IM from another agent - MessageFromAgent = 0, - /// Simple notification box with an OK button. - MessageBox = 1, - /// You've been invited to join a group. - GroupInvitation = 3, - /// Indicates that someone has given the user an object - /// Soon to be deprecated - GiveInventory = 4, - /// Inventory offer - InventoryOffered = 4, - /// An avatar has accepted your inventory offer - InventoryAccepted = 5, - /// An avatar has declined your inventory offer - InventoryDeclined = 6, - /// Group vote - GroupVote = 7, - /// Indicates that someone has given us a notecard - GiveNotecard = 9, - /// Unknown - TaskInventoryOffered = 9, - /// Unknown - TaskInventoryAccepted = 10, - /// Unknown - TaskInventoryDeclined = 11, - /// Unknown - NewUserDefault = 12, - /// Start a session, or add users to a session - SessionAdd = 13, - /// Start a session, but don't prune offline users - SessionOfflineAdd = 14, - /// Start a session with your group - SessionGroupStart = 15, - /// Start a session without a calling card (finder or objects) - SessionCardlessStart = 16, - /// Send a message to a session - SessionSend = 17, - /// Leave a session - SessionDrop = 18, - /// Indicates that the IM is from an object - MessageFromObject = 19, - /// sent an IM to a busy user, this is the auto response - BusyAutoResponse = 20, - /// Shows the message in the console and chat history - ConsoleAndChatHistory = 21, - /// IM Types used for luring your friends - RequestTeleport = 22, - /// Response sent to the agent which inititiated a teleport invitation - AcceptTeleport = 23, - /// Response sent to the agent which inititiated a teleport invitation - DenyTeleport = 24, - /// Only useful if you have Linden permissions - GodLikeRequestTeleport = 25, - /// IM to tell the user to go to an URL - GotoUrl = 28, - /// IM for help - Session911Start = 29, - /// IM sent automatically on call for help, sends a lure - /// to each Helper reached - Lure911 = 30, - /// Like an IM but won't go to email - FromTaskAsAlert = 31, - /// IM from a group officer to all group members - GroupNotice = 32, - /// Unknown - GroupNoticeInventoryAccepted = 33, - /// Unknown - GroupNoticeInventoryDeclined = 34, - /// Accept a group invitation - GroupInvitationAccept = 35, - /// Decline a group invitation - GroupInvitationDecline = 36, - /// Unknown - GroupNoticeRequested = 37, - /// An avatar is offering you friendship - FriendshipOffered = 38, - /// An avatar has accepted your friendship offer - FriendshipAccepted = 39, - /// An avatar has declined your friendship offer - FriendshipDeclined = 40, - /// Indicates that a user has started typing - StartTyping = 41, - /// Indicates that a user has stopped typing - StopTyping = 42 - } - - /// - /// Conversion type to denote Chat Packet types in an easier-to-understand format - /// - public enum ChatType : byte - { - /// Whispers (5m radius) - Whisper = 0, - /// Normal chat (10/20m radius), what the official viewer typically sends - Normal = 1, - /// Shouting! (100m radius) - Shout = 2, - /// Say chat (10/20m radius) - The official viewer will - /// print "[4:15] You say, hey" instead of "[4:15] You: hey" - Say = 3, - /// Event message when an Avatar has begun to type - StartTyping = 4, - /// Event message when an Avatar has stopped typing - StopTyping = 5 - } - - /// - /// Effect type used in ViewerEffect packets - /// - public enum EffectType : byte - { - /// Place floating text above an object - Text = 0, - /// Unknown, probably places an icon above an object - Icon, - /// Unknown - Connector, - /// Unknown - FlexibleObject, - /// Unknown - AnimalControls, - /// Unknown - AnimationObject, - /// Unknown - Cloth, - /// Project a beam from a source to a destination, such as - /// the one used when editing an object - Beam, - /// Not implemented yet - Glow, - /// Unknown - Point, - /// Unknown - Trail, - /// Create a swirl of particles around an object - Sphere, - /// Unknown - Spiral, - /// Unknown - Edit, - /// Cause an avatar to look at an object - LookAt, - /// Cause an avatar to point at an object - PointAt - } - - /// - /// The action an avatar is doing when looking at something, used in - /// ViewerEffect packets for the LookAt effect - /// - public enum LookAtTarget : byte - { - /// - None, - /// - Idle, - /// - AutoListen, - /// - FreeLook, - /// - Respond, - /// - Hover, - /// Deprecated - Conversation, - /// - Select, - /// - Focus, - /// - Mouselook, - /// - Clear - } - - /// - /// The action an avatar is doing when pointing at something, used in - /// ViewerEffect packets for the PointAt effect - /// - public enum PointAtType : byte - { - /// - None, - /// - Select, - /// - Grab, - /// - Clear - } - - - /// - /// Triggered on incoming chat messages - /// - /// Text of chat message - /// Is this normal audible chat or not. - /// Type of chat (whisper,shout,status,etc) - /// Type of source (Agent / Object / ???) - /// Text name of sending Avatar/Object - /// - public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype, - string fromName, LLUUID id, LLUUID ownerid, LLVector3 position); - - /// - /// Triggered when a script pops up a dialog box - /// - /// The dialog box message - /// Name of the object that sent the dialog - /// Image to be displayed in the dialog - /// ID of the object that sent the dialog - /// First name of the object owner - /// Last name of the object owner - /// Chat channel that the object is communicating on - /// List of button labels - public delegate void ScriptDialogCallback(string message, string objectName, LLUUID imageID, - LLUUID objectID, string firstName, string lastName, int chatChannel, List buttons); - - /// - /// Triggered when the L$ account balance for this avatar changes - /// - /// The new account balance - public delegate void BalanceCallback(int balance); - - /// - /// Tiggered on incoming instant messages - /// - /// Key of sender - /// Name of sender - /// Key of destination Avatar - /// ID of originating Estate - /// Key of originating Region - /// Coordinates in originating Region - /// - /// Group IM session toggle - /// Key of IM Session - /// Timestamp of message - /// Text of message - /// - /// - public delegate void InstantMessageCallback(LLUUID fromAgentID, string fromAgentName, - LLUUID toAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position, - byte dialog, bool groupIM, LLUUID imSessionID, DateTime timestamp, string message, - byte offline, byte[] binaryBucket); - - /// - /// Triggered for any status updates of a teleport (progress, failed, succeeded) - /// - /// The simulator the avatar is currently residing in - /// A message about the current teleport status - /// The current status of the teleport - public delegate void TeleportCallback(Simulator currentSim, string message, TeleportStatus status); - - /// - /// Reply to a request to join a group, informs whether it was successful or not - /// - /// The group we attempted to join - /// Whether we joined the group or not - public delegate void JoinGroupCallback(LLUUID groupID, bool success); - - /// - /// Reply to a request to leave a group, informs whether it was successful or not - /// - /// The group we attempted to leave - /// Whether we left the group or not - public delegate void LeaveGroupCallback(LLUUID groupID, bool success); - - /// - /// Informs the avatar that it is no longer a member of a group - /// - /// The group we are no longer a member of - public delegate void GroupDroppedCallback(LLUUID groupID); - - - /// Callback for incoming chat packets - public event ChatCallback OnChat; - /// Callback for pop-up dialogs from scripts - public event ScriptDialogCallback OnScriptDialog; - /// Callback for incoming IMs - public event InstantMessageCallback OnInstantMessage; - /// Callback for Teleport request update - public event TeleportCallback OnTeleport; - /// Callback for incoming change in L$ balance - public event BalanceCallback OnBalanceUpdated; - /// Callback reply for an attempt to join a group - public event JoinGroupCallback OnJoinGroup; - /// Callback reply for an attempt to leave a group - public event LeaveGroupCallback OnLeaveGroup; - /// Callback for informing the avatar that it is no longer a member of a group - public event GroupDroppedCallback OnGroupDropped; - - /// Your (client) Avatar UUID, asset server - public LLUUID ID = LLUUID.Zero; - /// Your (client) Avatar ID, local to Region/sim - public uint LocalID; - /// Avatar First Name (i.e. Philip) - public string FirstName = String.Empty; - /// Avatar Last Name (i.e. Linden) - public string LastName = String.Empty; - /// Positive and negative ratings - /// This information is read-only and any changes will not be - /// reflected on the server - public Avatar.Statistics ProfileStatistics = new Avatar.Statistics(); - /// Avatar properties including about text, profile URL, image IDs and - /// publishing settings - /// If you change fields in this struct, the changes will not - /// be reflected on the server until you call SetAvatarInformation - public Avatar.Properties ProfileProperties = new Avatar.Properties(); - /// Avatar interests including spoken languages, skills, and "want to" - /// choices - /// If you change fields in this struct, the changes will not - /// be reflected on the server until you call SetAvatarInformation - public Avatar.Interests ProfileInterests = new Avatar.Interests(); - /// Current position of avatar - public LLVector3 Position = LLVector3.Zero; - /// Current rotation of avatar - public LLQuaternion Rotation = LLQuaternion.Identity; - /// The point the avatar is currently looking at - /// (may not stay updated) - public LLVector3 LookAt = LLVector3.Zero; - /// Position avatar client will goto when login to 'home' or during - /// teleport request to 'home' region. - public LLVector3 HomePosition = LLVector3.Zero; - /// LookAt point saved/restored with HomePosition - public LLVector3 HomeLookAt = LLVector3.Zero; - /// Used for camera and control key state tracking - public MainAvatarStatus Status; - /// The UUID of your root inventory folder - public LLUUID InventoryRootFolderUUID; - - - /// Gets the health of the agent - public float Health - { - get { return health; } - } - - /// Gets the current balance of the agent - public int Balance - { - get { return balance; } - } - - /// Gets the local ID of the prim the avatar is sitting on, - /// zero if the avatar is not currently sitting - public uint SittingOn - { - get { return sittingOn; } - } - - internal uint sittingOn = 0; - internal string teleportMessage = String.Empty; - - private SecondLife Client; - private TeleportCallback OnBeginTeleport; - private TeleportStatus TeleportStat; - private Timer TeleportTimer; - private bool TeleportTimeout; - private uint HeightWidthGenCounter; - private float health = 0.0f; - private int balance = 0; - - /// - /// Constructor, setup callbacks for packets related to our avatar - /// - /// - public MainAvatar(SecondLife client) - { - NetworkManager.PacketCallback callback; - Client = client; - - Status = new MainAvatarStatus(Client); - - // Coarse location callback - Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, new NetworkManager.PacketCallback(CoarseLocationHandler)); - - // Teleport callbacks - callback = new NetworkManager.PacketCallback(TeleportHandler); - Client.Network.RegisterCallback(PacketType.TeleportStart, callback); - Client.Network.RegisterCallback(PacketType.TeleportProgress, callback); - Client.Network.RegisterCallback(PacketType.TeleportFailed, callback); - Client.Network.RegisterCallback(PacketType.TeleportFinish, callback); - - // Instant Message callback - Client.Network.RegisterCallback(PacketType.ImprovedInstantMessage, new NetworkManager.PacketCallback(InstantMessageHandler)); - - // Chat callback - Client.Network.RegisterCallback(PacketType.ChatFromSimulator, new NetworkManager.PacketCallback(ChatHandler)); - - // Script dialog callback - Client.Network.RegisterCallback(PacketType.ScriptDialog, new NetworkManager.PacketCallback(ScriptDialogHandler)); - - // Teleport timeout timer - TeleportTimer = new Timer(Client.Settings.TELEPORT_TIMEOUT); - TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); - TeleportTimeout = false; - - // Movement complete callback - Client.Network.RegisterCallback(PacketType.AgentMovementComplete, new NetworkManager.PacketCallback(MovementCompleteHandler)); - - // Health callback - Client.Network.RegisterCallback(PacketType.HealthMessage, new NetworkManager.PacketCallback(HealthHandler)); - - // Money callbacks - callback = new NetworkManager.PacketCallback(BalanceHandler); - Client.Network.RegisterCallback(PacketType.MoneyBalanceReply, callback); - Client.Network.RegisterCallback(PacketType.MoneySummaryReply, callback); - Client.Network.RegisterCallback(PacketType.AdjustBalance, callback); - - // Group callbacks - Client.Network.RegisterCallback(PacketType.JoinGroupReply, new NetworkManager.PacketCallback(JoinGroupHandler)); - Client.Network.RegisterCallback(PacketType.LeaveGroupReply, new NetworkManager.PacketCallback(LeaveGroupHandler)); - Client.Network.RegisterCallback(PacketType.AgentDropGroup, new NetworkManager.PacketCallback(DropGroupHandler)); - - // Viewer effect callback - Client.Network.RegisterCallback(PacketType.ViewerEffect, new NetworkManager.PacketCallback(ViewerEffectHandler)); - - // Event queue callback (used for Caps teleports currently) - Client.Network.RegisterEventCallback(new NetworkManager.EventQueueCallback(EventQueueHandler)); - } - - /// - /// Send an Instant Message - /// - /// Key of Avatar - /// Text Message being sent. - public void InstantMessage(LLUUID target, string message) - { - InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, LLUUID.Random()); - } - - /// - /// Send an Instant Message, used for dialog responses. - /// - /// Key of Avatar - /// Dialog code to be sent. - public void InstantMessage(LLUUID target, InstantMessageDialog dialog) - { - InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, String.Empty, null, LLUUID.Random(), dialog); - } - - /// - /// Send an Instant Message - /// - /// Key of Avatar - /// Text Message being sent. - /// IM Session ID - public void InstantMessage(LLUUID target, string message, LLUUID IMSessionID) - { - InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, IMSessionID); - } - - /// - /// Send an Instant Message - /// - /// Client's Avatar - /// SessionID of current connection to grid - /// Key of Avatar - /// Text Message being sent. - /// - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) - { - InstantMessage(fromName, sessionID, target, message, conferenceIDs, LLUUID.Random()); - } - - /// - /// Send an Instant Message - /// - /// The name this IM will show up as being from - /// Session ID of current connection to grid - /// Key of Avatar - /// Text message being sent - /// - /// IM session ID (to differentiate between IM windows) - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, - LLUUID[] conferenceIDs, LLUUID IMSessionID) - { - InstantMessage(fromName, sessionID, target, message, conferenceIDs, IMSessionID, InstantMessageDialog.MessageFromAgent); - } - - /// - /// Send an Instant Message - /// - /// The name this IM will show up as being from - /// Session ID of current connection to grid - /// Key of Avatar - /// Text message being sent - /// - /// IM session ID (to differentiate between IM windows) - public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, - LLUUID[] conferenceIDs, LLUUID IMSessionID, InstantMessageDialog dialog) - { - ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); - im.AgentData.AgentID = this.ID; - im.AgentData.SessionID = Client.Network.SessionID; - im.MessageBlock.Dialog = (byte)dialog; - im.MessageBlock.FromAgentName = Helpers.StringToField(fromName); - im.MessageBlock.FromGroup = false; - im.MessageBlock.ID = IMSessionID; - im.MessageBlock.Message = Helpers.StringToField(message); - im.MessageBlock.Offline = 1; - im.MessageBlock.ToAgentID = target; - if (conferenceIDs != null && conferenceIDs.Length > 0) - { - im.MessageBlock.BinaryBucket = new byte[16 * conferenceIDs.Length]; - - for (int i = 0; i < conferenceIDs.Length; ++i) - { - Array.Copy(conferenceIDs[i].Data, 0, im.MessageBlock.BinaryBucket, i * 16, 16); - } - } - else - { - im.MessageBlock.BinaryBucket = new byte[0]; - } - - // These fields are mandatory, even if we don't have valid values for them - im.MessageBlock.Position = LLVector3.Zero; - //TODO: Allow region id to be correctly set by caller or fetched from Client.* - im.MessageBlock.RegionID = LLUUID.Zero; - - // Send the message - Client.Network.SendPacket(im); - } - - /// - /// - /// - /// - /// - /// - /// - public void PointAtEffect(LLUUID sourceAvatar, LLUUID targetObject, LLVector3d globalOffset, PointAtType type) - { - ViewerEffectPacket effect = new ViewerEffectPacket(); - effect.Effect = new ViewerEffectPacket.EffectBlock[1]; - effect.Effect[0] = new ViewerEffectPacket.EffectBlock(); - effect.Effect[0].Color = LLColor.Black.GetBytes(); - effect.Effect[0].Duration = (type == PointAtType.Clear) ? 0.0f : Single.MaxValue / 4.0f; - effect.Effect[0].ID = LLUUID.Random(); - effect.Effect[0].Type = (byte)EffectType.PointAt; - - byte[] typeData = new byte[57]; - if (sourceAvatar != null) - Array.Copy(sourceAvatar.GetBytes(), typeData, 16); - if (targetObject != null) - Array.Copy(targetObject.GetBytes(), 0, typeData, 16, 16); - Array.Copy(globalOffset.GetBytes(), 0, typeData, 32, 24); - typeData[56] = (byte)type; - - effect.Effect[0].TypeData = typeData; - - Client.Network.SendPacket(effect); - } - - /// - /// - /// - /// - /// - /// - /// - /// - public void BeamEffect(LLUUID sourceAvatar, LLUUID targetObject, LLVector3d globalOffset, LLColor color, - float duration) - { - ViewerEffectPacket effect = new ViewerEffectPacket(); - effect.Effect = new ViewerEffectPacket.EffectBlock[1]; - effect.Effect[0] = new ViewerEffectPacket.EffectBlock(); - effect.Effect[0].Color = color.GetBytes(); - effect.Effect[0].Duration = duration; - effect.Effect[0].ID = LLUUID.Random(); - effect.Effect[0].Type = (byte)EffectType.Beam; - - byte[] typeData = new byte[56]; - if (sourceAvatar != null) - Array.Copy(sourceAvatar.GetBytes(), 0, typeData, 0, 16); - if (targetObject != null) - Array.Copy(targetObject.GetBytes(), 0, typeData, 16, 16); - Array.Copy(globalOffset.GetBytes(), 0, typeData, 32, 24); - - effect.Effect[0].TypeData = typeData; - - Client.Network.SendPacket(effect); - } - - /// - /// - /// - public void LookAtEffect() - { - } - - /// - /// Synchronize the local profile and interests information to the server - /// - public void SetAvatarInformation() - { - // Basic profile properties - AvatarPropertiesUpdatePacket apup = new AvatarPropertiesUpdatePacket(); - - apup.AgentData.AgentID = this.ID; - apup.AgentData.SessionID = Client.Network.SessionID; - apup.PropertiesData.AboutText = Helpers.StringToField(this.ProfileProperties.AboutText); - apup.PropertiesData.AllowPublish = this.ProfileProperties.AllowPublish; - apup.PropertiesData.FLAboutText = Helpers.StringToField(this.ProfileProperties.FirstLifeText); - apup.PropertiesData.FLImageID = this.ProfileProperties.FirstLifeImage; - apup.PropertiesData.ImageID = this.ProfileProperties.ProfileImage; - apup.PropertiesData.MaturePublish = this.ProfileProperties.MaturePublish; - apup.PropertiesData.ProfileURL = Helpers.StringToField(this.ProfileProperties.ProfileURL); - - // Interests - AvatarInterestsUpdatePacket aiup = new AvatarInterestsUpdatePacket(); - - aiup.AgentData.AgentID = this.ID; - aiup.AgentData.SessionID = Client.Network.SessionID; - aiup.PropertiesData.LanguagesText = Helpers.StringToField(this.ProfileInterests.LanguagesText); - aiup.PropertiesData.SkillsMask = this.ProfileInterests.SkillsMask; - aiup.PropertiesData.SkillsText = Helpers.StringToField(this.ProfileInterests.SkillsText); - aiup.PropertiesData.WantToMask = this.ProfileInterests.WantToMask; - aiup.PropertiesData.WantToText = Helpers.StringToField(this.ProfileInterests.WantToText); - - //Send packets - Client.Network.SendPacket(apup); - Client.Network.SendPacket(aiup); - } - - /// - /// Send a chat message - /// - /// The Message you're sending out. - /// Channel number (0 would be default 'Say' message, other numbers - /// denote the equivalent of /# in normal client). - /// Chat Type, see above. - public void Chat(string message, int channel, ChatType type) - { - ChatFromViewerPacket chat = new ChatFromViewerPacket(); - chat.AgentData.AgentID = this.ID; - chat.AgentData.SessionID = Client.Network.SessionID; - chat.ChatData.Channel = channel; - chat.ChatData.Message = Helpers.StringToField(message); - chat.ChatData.Type = (byte)type; - - Client.Network.SendPacket(chat); - } - - /// - /// Set the height and the width of the client window. This is used - /// by the server to build a virtual camera frustum for our avatar - /// - /// New height of the viewer window - /// New width of the viewer window - public void SetHeightWidth(ushort height, ushort width) - { - AgentHeightWidthPacket heightwidth = new AgentHeightWidthPacket(); - heightwidth.AgentData.AgentID = Client.Network.AgentID; - heightwidth.AgentData.SessionID = Client.Network.SessionID; - heightwidth.AgentData.CircuitCode = Client.Network.CurrentSim.CircuitCode; - heightwidth.HeightWidthBlock.Height = height; - heightwidth.HeightWidthBlock.Width = width; - heightwidth.HeightWidthBlock.GenCounter = HeightWidthGenCounter++; - - Client.Network.SendPacket(heightwidth); - } - - /// - /// Sends a request to sit on the specified object - /// - /// LLUUID of the object to sit on - /// Sit at offset - public void RequestSit(LLUUID targetID, LLVector3 offset) - { - AgentRequestSitPacket requestSit = new AgentRequestSitPacket(); - requestSit.AgentData.AgentID = Client.Network.AgentID; - requestSit.AgentData.SessionID = Client.Network.SessionID; - requestSit.TargetObject.TargetID = targetID; - requestSit.TargetObject.Offset = offset; - Client.Network.SendPacket(requestSit); - } - - /// - /// Request the list of muted things for this avatar - /// - public void RequestMuteList() - { - MuteListRequestPacket mute = new MuteListRequestPacket(); - mute.AgentData.AgentID = Client.Network.AgentID; - mute.AgentData.SessionID = Client.Network.SessionID; - mute.MuteData.MuteCRC = 0; - - Client.Network.SendPacket(mute); - } - - /// - /// Request the current L$ balance - /// - public void RequestBalance() - { - MoneyBalanceRequestPacket money = new MoneyBalanceRequestPacket(); - money.AgentData.AgentID = Client.Network.AgentID; - money.AgentData.SessionID = Client.Network.SessionID; - money.MoneyData.TransactionID = LLUUID.Zero; - - Client.Network.SendPacket(money); - } - - /// - /// Set the always running toggle on the server - /// - /// Whether the avatar should always run or not - public void SetAlwaysRun(bool alwaysRun) - { - SetAlwaysRunPacket run = new SetAlwaysRunPacket(); - run.AgentData.AgentID = Client.Network.AgentID; - run.AgentData.SessionID = Client.Network.SessionID; - run.AgentData.AlwaysRun = alwaysRun; - - Client.Network.SendPacket(run); - } - - /// - /// Follows a call to RequestSit() to actually sit on the object - /// - public void Sit() - { - AgentSitPacket sit = new AgentSitPacket(); - sit.AgentData.AgentID = Client.Network.AgentID; - sit.AgentData.SessionID = Client.Network.SessionID; - Client.Network.SendPacket(sit); - } - - /// - /// Give Money to destination Avatar - /// - /// UUID of the Target Avatar - /// Amount in L$ - /// Reason (optional normally) - public void GiveMoney(LLUUID target, int amount, string description) - { - // 5001 - transaction type for av to av money transfers - if (amount > 0) - GiveMoney(target, amount, description, 5001); - else - Client.Log("Attempted to pay zero or negative value " + amount, Helpers.LogLevel.Warning); - } - - /// - /// Give Money to destionation Object or Avatar - /// - /// UUID of the Target Object/Avatar - /// Amount in L$ - /// Reason (Optional normally) - /// The type of transaction. Currently only 5001 is - /// documented for Av->Av money transfers. - public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) - { - MoneyTransferRequestPacket money = new MoneyTransferRequestPacket(); - money.AgentData.AgentID = this.ID; - money.AgentData.SessionID = Client.Network.SessionID; - money.MoneyData.Description = Helpers.StringToField(description); - money.MoneyData.DestID = target; - money.MoneyData.SourceID = this.ID; - money.MoneyData.TransactionType = transactiontype; - money.MoneyData.AggregatePermInventory = 0; //TODO: whats this? - money.MoneyData.AggregatePermNextOwner = 0; //TODO: whats this? - money.MoneyData.Flags = 0; //TODO: whats this? - money.MoneyData.Amount = amount; - - Client.Network.SendPacket(money); - } - - /// - /// Send an AgentAnimation packet that toggles a single animation on - /// - /// The animation to start playing - public void AnimationStart(LLUUID animation) - { - Dictionary animations = new Dictionary(); - animations[animation] = true; - - Animate(animations); - } - - /// - /// Send an AgentAnimation packet that toggles a single animation off - /// - /// The animation to stop playing - public void AnimationStop(LLUUID animation) - { - Dictionary animations = new Dictionary(); - animations[animation] = false; - - Animate(animations); - } - - /// - /// Send an AgentAnimation packet that will toggle animations on or off - /// - /// A list of animation UUIDs, and whether to - /// turn that animation on or off - public void Animate(Dictionary animations) - { - AgentAnimationPacket animate = new AgentAnimationPacket(); - - animate.AgentData.AgentID = Client.Network.AgentID; - animate.AgentData.SessionID = Client.Network.SessionID; - animate.AnimationList = new AgentAnimationPacket.AnimationListBlock[animations.Count]; - int i = 0; - - foreach (KeyValuePair animation in animations) - { - animate.AnimationList[i] = new AgentAnimationPacket.AnimationListBlock(); - animate.AnimationList[i].AnimID = animation.Key; - animate.AnimationList[i].StartAnim = animation.Value; - - i++; - } - - Client.Network.SendPacket(animate); - } - - /// - /// Use the autopilot sim function to move the avatar to a new position - /// - /// The z value is currently not handled properly by the simulator - /// Integer value for the global X coordinate to move to - /// Integer value for the global Y coordinate to move to - /// Floating-point value for the Z coordinate to move to - /// AutoPilot(252620, 247078, 20.2674); - public void AutoPilot(ulong globalX, ulong globalY, float z) - { - GenericMessagePacket autopilot = new GenericMessagePacket(); - - autopilot.AgentData.AgentID = Client.Network.AgentID; - autopilot.AgentData.SessionID = Client.Network.SessionID; - autopilot.MethodData.Invoice = LLUUID.Zero; - autopilot.MethodData.Method = Helpers.StringToField("autopilot"); - autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3]; - autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock(); - autopilot.ParamList[0].Parameter = Helpers.StringToField(globalX.ToString()); - autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock(); - autopilot.ParamList[1].Parameter = Helpers.StringToField(globalY.ToString()); - autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock(); - // TODO: Do we need to prevent z coordinates from being sent in 1.4827e-18 notation? - autopilot.ParamList[2].Parameter = Helpers.StringToField(z.ToString()); - - Client.Network.SendPacket(autopilot); - } - - /// - /// Use the autopilot sim function to move the avatar to a new position - /// - /// The z value is currently not handled properly by the simulator - /// Integer value for the local X coordinate to move to - /// Integer value for the local Y coordinate to move to - /// Floating-point value for the Z coordinate to move to - /// AutoPilot(252620, 247078, 20.2674); - public void AutoPilotLocal(int localX, int localY, float z) - { - uint x, y; - Helpers.LongToUInts(Client.Network.CurrentSim.Region.Handle, out x, out y); - AutoPilot((ulong)(x + localX), (ulong)(y + localY), z); - } - - /// - /// Start a teleport process - /// - /// - /// Position for Teleport - /// Callback ID - public void BeginTeleport(ulong regionHandle, LLVector3 position, TeleportCallback tc) - { - BeginTeleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z), tc); - } - - /// - /// Start a teleport process - /// - /// - /// Position for Teleport - /// Target to look at - /// Callback ID - public void BeginTeleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt, TeleportCallback tc) - { - OnBeginTeleport = tc; - - TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); - teleport.AgentData.AgentID = Client.Network.AgentID; - teleport.AgentData.SessionID = Client.Network.SessionID; - teleport.Info.LookAt = lookAt; - teleport.Info.Position = position; - teleport.Info.RegionHandle = regionHandle; - - Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); - - Client.Network.SendPacket(teleport); - } - - /// - /// Start a teleport process - /// - /// - /// Position for Teleport - /// - public bool Teleport(ulong regionHandle, LLVector3 position) - { - return Teleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z)); - } - - /// - /// Start a teleport process - /// - /// - /// Position for Teleport - /// Target to look at - /// - public bool Teleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt) - { - TeleportStat = TeleportStatus.None; - - TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); - teleport.AgentData.AgentID = Client.Network.AgentID; - teleport.AgentData.SessionID = Client.Network.SessionID; - teleport.Info.LookAt = lookAt; - teleport.Info.Position = position; - - teleport.Info.RegionHandle = regionHandle; - - Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); - - // Start the timeout check - TeleportTimeout = false; - TeleportTimer.Start(); - - Client.Network.SendPacket(teleport); - - // FIXME: Use a ManualResetEvent, Client.Tick() is bad - while (TeleportStat != TeleportStatus.Failed && TeleportStat != TeleportStatus.Finished && !TeleportTimeout) - { - Client.Tick(); - } - - TeleportTimer.Stop(); - - if (TeleportTimeout) - { - teleportMessage = "Teleport timed out."; - TeleportStat = TeleportStatus.Failed; - - if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); } - } - else - { - if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); } - } - - return (TeleportStat == TeleportStatus.Finished); - } - - /// - /// Generic Teleport Function - /// - /// Region name - /// Position for Teleport - /// - public bool Teleport(string simName, LLVector3 position) - { - //position.Z = 0; //why was this here? - return Teleport(simName, position, new LLVector3(0, 1.0F, 0)); - } - - /// - /// Teleport Function - /// - /// Region name - /// Position for Teleport - /// Target to look at - /// - public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) - { - int attempts = 0; - TeleportStat = TeleportStatus.None; - - simName = simName.ToLower(); - - GridRegion region = Client.Grid.GetGridRegion(simName); - - if (region != null) - { - return Teleport(region.RegionHandle, position, lookAt); - } - else - { - while (attempts++ < 5) - { - region = Client.Grid.GetGridRegion(simName); - - if (region != null) - { - return Teleport(region.RegionHandle, position, lookAt); - } - else - { - // Request the region info again - Client.Grid.AddSim(simName); - - // FIXME: We shouldn't be sleeping in the library at all, hopefully this goes away soon - System.Threading.Thread.Sleep(1000); - } - } - } - - if (OnTeleport != null) - { - teleportMessage = "Unable to resolve name: " + simName; - TeleportStat = TeleportStatus.Failed; - OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); - } - - return false; - } - - /// - /// Respond to a teleport lure by either accepting it and initiating - /// the teleport, or denying it - /// - /// UUID of the avatar requesting the teleport - /// Accept the teleport request or deny it - public void TeleportLureRespond(LLUUID requesterID, bool accept) - { - InstantMessage(requesterID, accept ? InstantMessageDialog.AcceptTeleport : InstantMessageDialog.DenyTeleport); - - if (accept) - { - TeleportLureRequestPacket lure = new TeleportLureRequestPacket(); - - lure.Info.AgentID = Client.Network.AgentID; - lure.Info.SessionID = Client.Network.SessionID; - lure.Info.LureID = Client.Network.AgentID; - lure.Info.TeleportFlags = 4; // TODO: What does this mean? - - Client.Network.SendPacket(lure); - } - } - - /// - /// Grabs an object - /// - /// Local ID of Object to grab - public void Grab(uint objectLocalID) - { - ObjectGrabPacket grab = new ObjectGrabPacket(); - grab.AgentData.AgentID = Client.Network.AgentID; - grab.AgentData.SessionID = Client.Network.SessionID; - grab.ObjectData.LocalID = objectLocalID; - grab.ObjectData.GrabOffset = new LLVector3(0, 0, 0); - Client.Network.SendPacket(grab); - } - - /// - /// Drags on an object - /// - /// Strangely, LLUID instead of local ID - /// Drag target in region coordinates - public void GrabUpdate(LLUUID objectID, LLVector3 grabPosition) - { - ObjectGrabUpdatePacket grab = new ObjectGrabUpdatePacket(); - grab.AgentData.AgentID = Client.Network.AgentID; - grab.AgentData.SessionID = Client.Network.SessionID; - grab.ObjectData.ObjectID = objectID; - grab.ObjectData.GrabOffsetInitial = new LLVector3(0, 0, 0); - grab.ObjectData.GrabPosition = grabPosition; - grab.ObjectData.TimeSinceLast = 0; - Client.Network.SendPacket(grab); - } - - /// - /// Releases a grabbed object - /// - public void DeGrab(uint objectLocalID) - { - ObjectDeGrabPacket degrab = new ObjectDeGrabPacket(); - degrab.AgentData.AgentID = Client.Network.AgentID; - degrab.AgentData.SessionID = Client.Network.SessionID; - degrab.ObjectData.LocalID = objectLocalID; - Client.Network.SendPacket(degrab); - } - - /// - /// Touches an object - /// - public void Touch(uint objectLocalID) - { - Client.Self.Grab(objectLocalID); - Client.Self.DeGrab(objectLocalID); - } - - /// - /// Request to join a group. If there is an enrollment fee it will - /// automatically be deducted from your balance - /// - /// The group to attempt to join - public void RequestJoinGroup(LLUUID groupID) - { - JoinGroupRequestPacket join = new JoinGroupRequestPacket(); - - join.AgentData.AgentID = Client.Network.AgentID; - join.AgentData.SessionID = Client.Network.SessionID; - join.GroupData.GroupID = groupID; - - Client.Network.SendPacket(join); - } - - /// - /// Request to leave a group - /// - /// The group to attempt to leave - public void RequestLeaveGroup(LLUUID groupID) - { - LeaveGroupRequestPacket leave = new LeaveGroupRequestPacket(); - - leave.AgentData.AgentID = Client.Network.AgentID; - leave.AgentData.SessionID = Client.Network.SessionID; - leave.GroupData.GroupID = groupID; - - Client.Network.SendPacket(leave); - } - - /// - /// Set our current active group - /// - /// The group we are a member of that we want to - /// activate - public void ActivateGroup(LLUUID groupID) - { - ActivateGroupPacket activate = new ActivateGroupPacket(); - - activate.AgentData.AgentID = Client.Network.AgentID; - activate.AgentData.SessionID = Client.Network.SessionID; - activate.AgentData.GroupID = groupID; - - Client.Network.SendPacket(activate); - } - - /// - /// Move an agent in to a simulator. This packet is the last packet - /// needed to complete the transition in to a new simulator - /// - /// - public void CompleteAgentMovement(Simulator simulator) - { - CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); - - move.AgentData.AgentID = Client.Network.AgentID; - move.AgentData.SessionID = Client.Network.SessionID; - move.AgentData.CircuitCode = simulator.CircuitCode; - - Client.Network.SendPacket(move, simulator); - } - - /// - /// Sends camera and action updates to the server including the - /// position and orientation of our camera, and a ControlFlags field - /// specifying our current movement actions - /// - /// Whether to ensure this packet makes it to the server - public void UpdateCamera(Avatar.AgentUpdateFlags controlFlags, LLVector3 position, LLVector3 forwardAxis, - LLVector3 leftAxis, LLVector3 upAxis, LLQuaternion bodyRotation, LLQuaternion headRotation, float farClip, - bool reliable) - { - AgentUpdatePacket update = new AgentUpdatePacket(); - - update.AgentData.AgentID = Client.Network.AgentID; - update.AgentData.SessionID = Client.Network.SessionID; - update.AgentData.State = 0; - update.AgentData.BodyRotation = bodyRotation; - update.AgentData.HeadRotation = headRotation; - update.AgentData.CameraCenter = position; - update.AgentData.CameraAtAxis = forwardAxis; - update.AgentData.CameraLeftAxis = leftAxis; - update.AgentData.CameraUpAxis = upAxis; - update.AgentData.Far = farClip; - update.AgentData.ControlFlags = (uint)controlFlags; - update.AgentData.Flags = 0; - update.Header.Reliable = reliable; - - Client.Network.SendPacket(update); - } - - /// - /// [UNUSED - for now] - /// - /// - /// - private void CoarseLocationHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.CoarseLocationUpdate) - { - CoarseLocationUpdatePacket p = (CoarseLocationUpdatePacket) packet; - - if (p.Index.You < 0 || p.Index.You >= p.Location.Length) return; - /* 1.5 and 6 represent a 50% fudge factor (hysteresis) -- bushing */ - if (Math.Abs(Position.X-p.Location[p.Index.You].X) > 1.5) Position.X=p.Location[p.Index.You].X; - if (Math.Abs(Position.Y-p.Location[p.Index.You].Y) > 1.5) Position.Y=p.Location[p.Index.You].Y; - if (Math.Abs(Position.Z-(p.Location[p.Index.You].Z*4)) > 6) Position.Z=p.Location[p.Index.You].Z*4; - } - } - - /// - /// Take an incoming ImprovedInstantMessage packet, auto-parse, and if - /// OnInstantMessage is defined call that with the appropriate arguments. - /// - /// Incoming ImprovedInstantMessagePacket - /// Unused - private void InstantMessageHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.ImprovedInstantMessage) - { - ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet; - - if (OnInstantMessage != null) - { - OnInstantMessage( - im.AgentData.AgentID - , Helpers.FieldToString(im.MessageBlock.FromAgentName), - im.MessageBlock.ToAgentID - , im.MessageBlock.ParentEstateID - , im.MessageBlock.RegionID - , im.MessageBlock.Position - , im.MessageBlock.Dialog - , im.MessageBlock.FromGroup - , im.MessageBlock.ID - , new DateTime(im.MessageBlock.Timestamp) - , Helpers.FieldToString(im.MessageBlock.Message) - , im.MessageBlock.Offline - , im.MessageBlock.BinaryBucket - ); - } - } - } - - /// - /// Take an incoming Chat packet, auto-parse, and if OnChat is defined call - /// that with the appropriate arguments. - /// - /// Incoming ChatFromSimulatorPacket - /// Unused - private void ChatHandler(Packet packet, Simulator simulator) - { - if (OnChat != null) - { - ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet; - - OnChat(Helpers.FieldToFilteredString(chat.ChatData.Message, new char()) - , chat.ChatData.Audible - , chat.ChatData.ChatType - , chat.ChatData.SourceType - , Helpers.FieldToString(chat.ChatData.FromName) - , chat.ChatData.SourceID - , chat.ChatData.OwnerID - , chat.ChatData.Position - ); - } - } - - /// - /// Used for parsing llDialog's - /// - /// Incoming ScriptDialog packet - /// Unused - private void ScriptDialogHandler(Packet packet, Simulator simulator) - { - if (OnScriptDialog != null) - { - ScriptDialogPacket dialog = (ScriptDialogPacket)packet; - List buttons = new List(); - - foreach (ScriptDialogPacket.ButtonsBlock button in dialog.Buttons) - { - buttons.Add(Helpers.FieldToString(button.ButtonLabel)); - } - - OnScriptDialog(Helpers.FieldToString(dialog.Data.Message), - Helpers.FieldToString(dialog.Data.ObjectName), - dialog.Data.ImageID, - dialog.Data.ObjectID, - Helpers.FieldToString(dialog.Data.FirstName), - Helpers.FieldToString(dialog.Data.LastName), - dialog.Data.ChatChannel, - buttons); - } - } - - /// - /// Update client's Position, LookAt and region handle from incoming packet - /// - /// Incoming AgentMovementCompletePacket - /// Unused - private void MovementCompleteHandler(Packet packet, Simulator simulator) - { - AgentMovementCompletePacket movement = (AgentMovementCompletePacket)packet; - - this.Position = movement.Data.Position; - this.LookAt = movement.Data.LookAt; - simulator.Region.Handle = movement.Data.RegionHandle; - } - - /// - /// Update Client Avatar's health via incoming packet - /// - /// Incoming HealthMessagePacket - /// Unused - private void HealthHandler(Packet packet, Simulator simulator) - { - health = ((HealthMessagePacket)packet).HealthData.Health; - } - - private void JoinGroupHandler(Packet packet, Simulator simulator) - { - if (OnJoinGroup != null) - { - JoinGroupReplyPacket reply = (JoinGroupReplyPacket)packet; - - OnJoinGroup(reply.GroupData.GroupID, reply.GroupData.Success); - } - } - - private void LeaveGroupHandler(Packet packet, Simulator simulator) - { - if (OnLeaveGroup != null) - { - LeaveGroupReplyPacket reply = (LeaveGroupReplyPacket)packet; - - OnLeaveGroup(reply.GroupData.GroupID, reply.GroupData.Success); - } - } - - private void DropGroupHandler(Packet packet, Simulator simulator) - { - if (OnGroupDropped != null) - { - OnGroupDropped(((AgentDropGroupPacket)packet).AgentData.GroupID); - } - } - - /// - /// Update Client Avatar's L$ balance from incoming packet - /// - /// Incoming MoneyBalanceReplyPacket - /// Unused - private void BalanceHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.MoneyBalanceReply) - { - balance = ((MoneyBalanceReplyPacket)packet).MoneyData.MoneyBalance; - } - else if (packet.Type == PacketType.MoneySummaryReply) - { - balance = ((MoneySummaryReplyPacket)packet).MoneyData.Balance; - } - else if (packet.Type == PacketType.AdjustBalance) - { - balance += ((AdjustBalancePacket)packet).AgentData.Delta; - } - - if (OnBalanceUpdated != null) - { - OnBalanceUpdated(balance); - } - } - - private void EventQueueHandler(string message, object body) - { - if(message == "TeleportFinish") - { - Hashtable tpt = (Hashtable)body; - Hashtable info = (Hashtable)tpt["Info"]; - - // FIXME: quick and dirty hack - TeleportFinishPacket packet = new TeleportFinishPacket(); - - packet.Info.SimIP = Helpers.BytesToUInt((byte[])info["SimIP"]); - packet.Info.LocationID = Helpers.BytesToUInt((byte[])info["LocationID"]); - packet.Info.TeleportFlags = Helpers.BytesToUInt((byte[])info["TeleportFlags"]); - packet.Info.AgentID = (LLUUID)info["AgentID"]; - packet.Info.RegionHandle = Helpers.BytesToUInt64((byte[])info["RegionHandle"]); - packet.Info.SeedCapability = Helpers.StringToField((string)info["SeedCapability"]); - packet.Info.SimPort = (ushort)(long)info["SimPort"]; - packet.Info.SimAccess = (byte)(long)info["SimAccess"]; - - TeleportHandler(packet,Client.Network.CurrentSim); - } - } - - /// - /// Process an incoming effect - /// - private void ViewerEffectHandler(Packet packet, Simulator simulator) - { - ViewerEffectPacket effect = (ViewerEffectPacket)packet; - - foreach (ViewerEffectPacket.EffectBlock block in effect.Effect) - { - EffectType type; - - try - { - type = (EffectType)block.Type; - } - catch (Exception) - { - Client.Log("Received a ViewerEffect block with an unknown type " + block.Type, - Helpers.LogLevel.Warning); - continue; - } - - //LLColor color; - //if (block.Color.Length == 4) - //{ - // color = new LLColor(block.Color, 0); - //} - //else - //{ - // Client.Log("Received a ViewerEffect.EffectBlock.Color array with " + block.Color.Length + " bytes", - // Helpers.LogLevel.Warning); - // color = new LLColor(); - //} - - // Each ViewerEffect type uses it's own custom binary format for additional data. Fun eh? - switch (type) - { - case EffectType.Text: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Icon: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Connector: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.FlexibleObject: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.AnimalControls: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.AnimationObject: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Cloth: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Beam: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Glow: - Client.Log("Received a Glow ViewerEffect which is not implemented yet", - Helpers.LogLevel.Warning); - break; - case EffectType.Point: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Trail: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Sphere: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Spiral: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.Edit: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.LookAt: - Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); - break; - case EffectType.PointAt: - if (block.TypeData.Length == 57) - { - LLUUID sourceAvatar = new LLUUID(block.TypeData, 0); - LLUUID targetObject = new LLUUID(block.TypeData, 16); - LLVector3d targetPos = new LLVector3d(block.TypeData, 32); - PointAtType pointAt; - try - { - pointAt = (PointAtType)block.TypeData[56]; - } - catch (Exception) - { - Client.Log("Unrecognized PointAtType " + block.TypeData[56], Helpers.LogLevel.Warning); - pointAt = PointAtType.Clear; - } - - // TODO: Create OnAvatarPointAt event and call it here - } - else - { - Client.Log("Received a PointAt ViewerEffect with an incorrect TypeData size of " + - block.TypeData.Length + " bytes", Helpers.LogLevel.Warning); - } - break; - } - } - } - - /// - /// Handler for teleport Requests - /// - /// Incoming TeleportHandler packet - /// Simulator sending teleport information - private void TeleportHandler(Packet packet, Simulator simulator) - { - if (packet.Type == PacketType.TeleportStart) - { - Client.DebugLog("TeleportStart received from " + simulator.ToString()); - - teleportMessage = "Teleport started"; - TeleportStat = TeleportStatus.Start; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); - } - } - else if (packet.Type == PacketType.TeleportProgress) - { - Client.DebugLog("TeleportProgress received from " + simulator.ToString()); - - teleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message); - TeleportStat = TeleportStatus.Progress; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); - } - } - else if (packet.Type == PacketType.TeleportFailed) - { - Client.DebugLog("TeleportFailed received from " + simulator.ToString()); - - teleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason); - TeleportStat = TeleportStatus.Failed; - - if (OnBeginTeleport != null) - { - OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); - } - - OnBeginTeleport = null; - } - else if (packet.Type == PacketType.TeleportFinish) - { - Client.DebugLog("TeleportFinish received from " + simulator.ToString()); - - TeleportFinishPacket finish = (TeleportFinishPacket)packet; - Simulator previousSim = Client.Network.CurrentSim; - - // Connect to the new sim - String seedcaps = Encoding.UTF8.GetString(finish.Info.SeedCapability).Replace("\x00",""); - Simulator sim = Client.Network.Connect(new IPAddress((long)finish.Info.SimIP), finish.Info.SimPort, - simulator.CircuitCode, true, seedcaps); - - if (sim != null) - { - teleportMessage = "Teleport finished"; - TeleportStat = TeleportStatus.Finished; - - // Move the avatar in to the new sim - CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); - move.AgentData.AgentID = Client.Network.AgentID; - move.AgentData.SessionID = Client.Network.SessionID; - move.AgentData.CircuitCode = simulator.CircuitCode; - Client.Network.SendPacket(move, sim); - - // Disconnect from the previous sim - Client.Network.DisconnectSim(previousSim); - - Client.Log("Moved to new sim " + sim.ToString(), Helpers.LogLevel.Info); - - if (OnBeginTeleport != null) - { - OnBeginTeleport(sim, teleportMessage, TeleportStat); - } - else - { - // Sleep a little while so we can collect parcel information - // NOTE: This doesn't belong in libsecondlife - // System.Threading.Thread.Sleep(1000); - } - } - else - { - teleportMessage = "Failed to connect to the new sim after a teleport"; - TeleportStat = TeleportStatus.Failed; - - // FIXME: Set the previous CurrentSim to the current simulator again - - Client.Log(teleportMessage, Helpers.LogLevel.Warning); - - if (OnBeginTeleport != null) - { - OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); - } - } - - OnBeginTeleport = null; - } - } - - /// - /// Teleport Timer Event Handler. Used for enforcing timeouts. - /// - /// - /// - private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) - { - TeleportTimeout = true; - } - } -} +/* + * Copyright (c) 2006, Second Life Reverse Engineering Team + * 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 Second Life Reverse Engineering Team 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.Timers; +using System.Net; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using libsecondlife.Packets; + +namespace libsecondlife +{ + /// + /// Class to hold Client Avatar's data + /// + public partial class MainAvatar + { + /// + /// Current teleport status + /// + public enum TeleportStatus + { + /// + None, + /// Teleport Start + Start, + /// Teleport in Progress + Progress, + /// Teleport Failed + Failed, + /// Teleport Completed + Finished + } + + /// + /// Special commands used in Instant Messages + /// + public enum InstantMessageDialog : byte + { + /// Indicates a regular IM from another agent + MessageFromAgent = 0, + /// Simple notification box with an OK button. + MessageBox = 1, + /// You've been invited to join a group. + GroupInvitation = 3, + /// Indicates that someone has given the user an object + /// Soon to be deprecated + GiveInventory = 4, + /// Inventory offer + InventoryOffered = 4, + /// An avatar has accepted your inventory offer + InventoryAccepted = 5, + /// An avatar has declined your inventory offer + InventoryDeclined = 6, + /// Group vote + GroupVote = 7, + /// Indicates that someone has given us a notecard + GiveNotecard = 9, + /// Unknown + TaskInventoryOffered = 9, + /// Unknown + TaskInventoryAccepted = 10, + /// Unknown + TaskInventoryDeclined = 11, + /// Unknown + NewUserDefault = 12, + /// Start a session, or add users to a session + SessionAdd = 13, + /// Start a session, but don't prune offline users + SessionOfflineAdd = 14, + /// Start a session with your group + SessionGroupStart = 15, + /// Start a session without a calling card (finder or objects) + SessionCardlessStart = 16, + /// Send a message to a session + SessionSend = 17, + /// Leave a session + SessionDrop = 18, + /// Indicates that the IM is from an object + MessageFromObject = 19, + /// sent an IM to a busy user, this is the auto response + BusyAutoResponse = 20, + /// Shows the message in the console and chat history + ConsoleAndChatHistory = 21, + /// IM Types used for luring your friends + RequestTeleport = 22, + /// Response sent to the agent which inititiated a teleport invitation + AcceptTeleport = 23, + /// Response sent to the agent which inititiated a teleport invitation + DenyTeleport = 24, + /// Only useful if you have Linden permissions + GodLikeRequestTeleport = 25, + /// IM to tell the user to go to an URL + GotoUrl = 28, + /// IM for help + Session911Start = 29, + /// IM sent automatically on call for help, sends a lure + /// to each Helper reached + Lure911 = 30, + /// Like an IM but won't go to email + FromTaskAsAlert = 31, + /// IM from a group officer to all group members + GroupNotice = 32, + /// Unknown + GroupNoticeInventoryAccepted = 33, + /// Unknown + GroupNoticeInventoryDeclined = 34, + /// Accept a group invitation + GroupInvitationAccept = 35, + /// Decline a group invitation + GroupInvitationDecline = 36, + /// Unknown + GroupNoticeRequested = 37, + /// An avatar is offering you friendship + FriendshipOffered = 38, + /// An avatar has accepted your friendship offer + FriendshipAccepted = 39, + /// An avatar has declined your friendship offer + FriendshipDeclined = 40, + /// Indicates that a user has started typing + StartTyping = 41, + /// Indicates that a user has stopped typing + StopTyping = 42 + } + + /// + /// Conversion type to denote Chat Packet types in an easier-to-understand format + /// + public enum ChatType : byte + { + /// Whispers (5m radius) + Whisper = 0, + /// Normal chat (10/20m radius), what the official viewer typically sends + Normal = 1, + /// Shouting! (100m radius) + Shout = 2, + /// Say chat (10/20m radius) - The official viewer will + /// print "[4:15] You say, hey" instead of "[4:15] You: hey" + Say = 3, + /// Event message when an Avatar has begun to type + StartTyping = 4, + /// Event message when an Avatar has stopped typing + StopTyping = 5 + } + + /// + /// Effect type used in ViewerEffect packets + /// + public enum EffectType : byte + { + /// Place floating text above an object + Text = 0, + /// Unknown, probably places an icon above an object + Icon, + /// Unknown + Connector, + /// Unknown + FlexibleObject, + /// Unknown + AnimalControls, + /// Unknown + AnimationObject, + /// Unknown + Cloth, + /// Project a beam from a source to a destination, such as + /// the one used when editing an object + Beam, + /// Not implemented yet + Glow, + /// Unknown + Point, + /// Unknown + Trail, + /// Create a swirl of particles around an object + Sphere, + /// Unknown + Spiral, + /// Unknown + Edit, + /// Cause an avatar to look at an object + LookAt, + /// Cause an avatar to point at an object + PointAt + } + + /// + /// The action an avatar is doing when looking at something, used in + /// ViewerEffect packets for the LookAt effect + /// + public enum LookAtTarget : byte + { + /// + None, + /// + Idle, + /// + AutoListen, + /// + FreeLook, + /// + Respond, + /// + Hover, + /// Deprecated + Conversation, + /// + Select, + /// + Focus, + /// + Mouselook, + /// + Clear + } + + /// + /// The action an avatar is doing when pointing at something, used in + /// ViewerEffect packets for the PointAt effect + /// + public enum PointAtType : byte + { + /// + None, + /// + Select, + /// + Grab, + /// + Clear + } + + + /// + /// Triggered on incoming chat messages + /// + /// Text of chat message + /// Is this normal audible chat or not. + /// Type of chat (whisper,shout,status,etc) + /// Type of source (Agent / Object / ???) + /// Text name of sending Avatar/Object + /// + public delegate void ChatCallback(string message, byte audible, byte type, byte sourcetype, + string fromName, LLUUID id, LLUUID ownerid, LLVector3 position); + + /// + /// Triggered when a script pops up a dialog box + /// + /// The dialog box message + /// Name of the object that sent the dialog + /// Image to be displayed in the dialog + /// ID of the object that sent the dialog + /// First name of the object owner + /// Last name of the object owner + /// Chat channel that the object is communicating on + /// List of button labels + public delegate void ScriptDialogCallback(string message, string objectName, LLUUID imageID, + LLUUID objectID, string firstName, string lastName, int chatChannel, List buttons); + + /// + /// Triggered when the L$ account balance for this avatar changes + /// + /// The new account balance + public delegate void BalanceCallback(int balance); + + /// + /// Tiggered on incoming instant messages + /// + /// Key of sender + /// Name of sender + /// Key of destination Avatar + /// ID of originating Estate + /// Key of originating Region + /// Coordinates in originating Region + /// + /// Group IM session toggle + /// Key of IM Session + /// Timestamp of message + /// Text of message + /// + /// + public delegate void InstantMessageCallback(LLUUID fromAgentID, string fromAgentName, + LLUUID toAgentID, uint parentEstateID, LLUUID regionID, LLVector3 position, + byte dialog, bool groupIM, LLUUID imSessionID, DateTime timestamp, string message, + byte offline, byte[] binaryBucket); + + /// + /// Triggered for any status updates of a teleport (progress, failed, succeeded) + /// + /// The simulator the avatar is currently residing in + /// A message about the current teleport status + /// The current status of the teleport + public delegate void TeleportCallback(Simulator currentSim, string message, TeleportStatus status); + + /// + /// Reply to a request to join a group, informs whether it was successful or not + /// + /// The group we attempted to join + /// Whether we joined the group or not + public delegate void JoinGroupCallback(LLUUID groupID, bool success); + + /// + /// Reply to a request to leave a group, informs whether it was successful or not + /// + /// The group we attempted to leave + /// Whether we left the group or not + public delegate void LeaveGroupCallback(LLUUID groupID, bool success); + + /// + /// Informs the avatar that it is no longer a member of a group + /// + /// The group we are no longer a member of + public delegate void GroupDroppedCallback(LLUUID groupID); + + + /// Callback for incoming chat packets + public event ChatCallback OnChat; + /// Callback for pop-up dialogs from scripts + public event ScriptDialogCallback OnScriptDialog; + /// Callback for incoming IMs + public event InstantMessageCallback OnInstantMessage; + /// Callback for Teleport request update + public event TeleportCallback OnTeleport; + /// Callback for incoming change in L$ balance + public event BalanceCallback OnBalanceUpdated; + /// Callback reply for an attempt to join a group + public event JoinGroupCallback OnJoinGroup; + /// Callback reply for an attempt to leave a group + public event LeaveGroupCallback OnLeaveGroup; + /// Callback for informing the avatar that it is no longer a member of a group + public event GroupDroppedCallback OnGroupDropped; + + /// Your (client) Avatar UUID, asset server + public LLUUID ID = LLUUID.Zero; + /// Your (client) Avatar ID, local to Region/sim + public uint LocalID; + /// Avatar First Name (i.e. Philip) + public string FirstName = String.Empty; + /// Avatar Last Name (i.e. Linden) + public string LastName = String.Empty; + /// Positive and negative ratings + /// This information is read-only and any changes will not be + /// reflected on the server + public Avatar.Statistics ProfileStatistics = new Avatar.Statistics(); + /// Avatar properties including about text, profile URL, image IDs and + /// publishing settings + /// If you change fields in this struct, the changes will not + /// be reflected on the server until you call SetAvatarInformation + public Avatar.Properties ProfileProperties = new Avatar.Properties(); + /// Avatar interests including spoken languages, skills, and "want to" + /// choices + /// If you change fields in this struct, the changes will not + /// be reflected on the server until you call SetAvatarInformation + public Avatar.Interests ProfileInterests = new Avatar.Interests(); + /// Current position of avatar + public LLVector3 Position = LLVector3.Zero; + /// Current rotation of avatar + public LLQuaternion Rotation = LLQuaternion.Identity; + /// The point the avatar is currently looking at + /// (may not stay updated) + public LLVector3 LookAt = LLVector3.Zero; + /// Position avatar client will goto when login to 'home' or during + /// teleport request to 'home' region. + public LLVector3 HomePosition = LLVector3.Zero; + /// LookAt point saved/restored with HomePosition + public LLVector3 HomeLookAt = LLVector3.Zero; + /// Used for camera and control key state tracking + public MainAvatarStatus Status; + /// The UUID of your root inventory folder + public LLUUID InventoryRootFolderUUID; + + + /// Gets the health of the agent + public float Health + { + get { return health; } + } + + /// Gets the current balance of the agent + public int Balance + { + get { return balance; } + } + + /// Gets the local ID of the prim the avatar is sitting on, + /// zero if the avatar is not currently sitting + public uint SittingOn + { + get { return sittingOn; } + } + + /// Gets the UUID of the active group. + public LLUUID ActiveGroup + { + get { return activeGroup; } + } + + internal uint sittingOn = 0; + internal string teleportMessage = String.Empty; + + private SecondLife Client; + private TeleportCallback OnBeginTeleport; + private TeleportStatus TeleportStat; + private Timer TeleportTimer; + private bool TeleportTimeout; + private uint HeightWidthGenCounter; + private float health = 0.0f; + private int balance = 0; + private LLUUID activeGroup; + + /// + /// Constructor, setup callbacks for packets related to our avatar + /// + /// + public MainAvatar(SecondLife client) + { + NetworkManager.PacketCallback callback; + Client = client; + + Status = new MainAvatarStatus(Client); + + // Coarse location callback + Client.Network.RegisterCallback(PacketType.CoarseLocationUpdate, new NetworkManager.PacketCallback(CoarseLocationHandler)); + + // Teleport callbacks + callback = new NetworkManager.PacketCallback(TeleportHandler); + Client.Network.RegisterCallback(PacketType.TeleportStart, callback); + Client.Network.RegisterCallback(PacketType.TeleportProgress, callback); + Client.Network.RegisterCallback(PacketType.TeleportFailed, callback); + Client.Network.RegisterCallback(PacketType.TeleportFinish, callback); + + // Instant Message callback + Client.Network.RegisterCallback(PacketType.ImprovedInstantMessage, new NetworkManager.PacketCallback(InstantMessageHandler)); + + // Chat callback + Client.Network.RegisterCallback(PacketType.ChatFromSimulator, new NetworkManager.PacketCallback(ChatHandler)); + + // Script dialog callback + Client.Network.RegisterCallback(PacketType.ScriptDialog, new NetworkManager.PacketCallback(ScriptDialogHandler)); + + // Teleport timeout timer + TeleportTimer = new Timer(Client.Settings.TELEPORT_TIMEOUT); + TeleportTimer.Elapsed += new ElapsedEventHandler(TeleportTimerEvent); + TeleportTimeout = false; + + // Movement complete callback + Client.Network.RegisterCallback(PacketType.AgentMovementComplete, new NetworkManager.PacketCallback(MovementCompleteHandler)); + + // Health callback + Client.Network.RegisterCallback(PacketType.HealthMessage, new NetworkManager.PacketCallback(HealthHandler)); + + // Money callbacks + callback = new NetworkManager.PacketCallback(BalanceHandler); + Client.Network.RegisterCallback(PacketType.MoneyBalanceReply, callback); + Client.Network.RegisterCallback(PacketType.MoneySummaryReply, callback); + Client.Network.RegisterCallback(PacketType.AdjustBalance, callback); + + // Group callbacks + Client.Network.RegisterCallback(PacketType.JoinGroupReply, new NetworkManager.PacketCallback(JoinGroupHandler)); + Client.Network.RegisterCallback(PacketType.LeaveGroupReply, new NetworkManager.PacketCallback(LeaveGroupHandler)); + Client.Network.RegisterCallback(PacketType.AgentDropGroup, new NetworkManager.PacketCallback(DropGroupHandler)); + + // Viewer effect callback + Client.Network.RegisterCallback(PacketType.ViewerEffect, new NetworkManager.PacketCallback(ViewerEffectHandler)); + + //Agent Update Callback + Client.Network.RegisterCallback(PacketType.AgentDataUpdate, new NetworkManager.PacketCallback(AgentDataUpdateHandler)); + + // Event queue callback (used for Caps teleports currently) + Client.Network.RegisterEventCallback(new NetworkManager.EventQueueCallback(EventQueueHandler)); + } + + /// + /// Send an Instant Message + /// + /// Key of Avatar + /// Text Message being sent. + public void InstantMessage(LLUUID target, string message) + { + InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, LLUUID.Random()); + } + + /// + /// Send an Instant Message, used for dialog responses. + /// + /// Key of Avatar + /// Dialog code to be sent. + public void InstantMessage(LLUUID target, InstantMessageDialog dialog) + { + InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, String.Empty, null, LLUUID.Random(), dialog); + } + + /// + /// Send an Instant Message + /// + /// Key of Avatar + /// Text Message being sent. + /// IM Session ID + public void InstantMessage(LLUUID target, string message, LLUUID IMSessionID) + { + InstantMessage(FirstName + " " + LastName, LLUUID.Random(), target, message, null, IMSessionID); + } + + /// + /// Send an Instant Message + /// + /// Client's Avatar + /// SessionID of current connection to grid + /// Key of Avatar + /// Text Message being sent. + /// + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, LLUUID[] conferenceIDs) + { + InstantMessage(fromName, sessionID, target, message, conferenceIDs, LLUUID.Random()); + } + + /// + /// Send an Instant Message + /// + /// The name this IM will show up as being from + /// Session ID of current connection to grid + /// Key of Avatar + /// Text message being sent + /// + /// IM session ID (to differentiate between IM windows) + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, + LLUUID[] conferenceIDs, LLUUID IMSessionID) + { + InstantMessage(fromName, sessionID, target, message, conferenceIDs, IMSessionID, InstantMessageDialog.MessageFromAgent); + } + + /// + /// Send an Instant Message + /// + /// The name this IM will show up as being from + /// Session ID of current connection to grid + /// Key of Avatar + /// Text message being sent + /// + /// IM session ID (to differentiate between IM windows) + public void InstantMessage(string fromName, LLUUID sessionID, LLUUID target, string message, + LLUUID[] conferenceIDs, LLUUID IMSessionID, InstantMessageDialog dialog) + { + ImprovedInstantMessagePacket im = new ImprovedInstantMessagePacket(); + im.AgentData.AgentID = this.ID; + im.AgentData.SessionID = Client.Network.SessionID; + im.MessageBlock.Dialog = (byte)dialog; + im.MessageBlock.FromAgentName = Helpers.StringToField(fromName); + im.MessageBlock.FromGroup = false; + im.MessageBlock.ID = IMSessionID; + im.MessageBlock.Message = Helpers.StringToField(message); + im.MessageBlock.Offline = 1; + im.MessageBlock.ToAgentID = target; + if (conferenceIDs != null && conferenceIDs.Length > 0) + { + im.MessageBlock.BinaryBucket = new byte[16 * conferenceIDs.Length]; + + for (int i = 0; i < conferenceIDs.Length; ++i) + { + Array.Copy(conferenceIDs[i].Data, 0, im.MessageBlock.BinaryBucket, i * 16, 16); + } + } + else + { + im.MessageBlock.BinaryBucket = new byte[0]; + } + + // These fields are mandatory, even if we don't have valid values for them + im.MessageBlock.Position = LLVector3.Zero; + //TODO: Allow region id to be correctly set by caller or fetched from Client.* + im.MessageBlock.RegionID = LLUUID.Zero; + + // Send the message + Client.Network.SendPacket(im); + } + + /// + /// + /// + /// + /// + /// + /// + public void PointAtEffect(LLUUID sourceAvatar, LLUUID targetObject, LLVector3d globalOffset, PointAtType type) + { + ViewerEffectPacket effect = new ViewerEffectPacket(); + effect.Effect = new ViewerEffectPacket.EffectBlock[1]; + effect.Effect[0] = new ViewerEffectPacket.EffectBlock(); + effect.Effect[0].Color = LLColor.Black.GetBytes(); + effect.Effect[0].Duration = (type == PointAtType.Clear) ? 0.0f : Single.MaxValue / 4.0f; + effect.Effect[0].ID = LLUUID.Random(); + effect.Effect[0].Type = (byte)EffectType.PointAt; + + byte[] typeData = new byte[57]; + if (sourceAvatar != null) + Array.Copy(sourceAvatar.GetBytes(), typeData, 16); + if (targetObject != null) + Array.Copy(targetObject.GetBytes(), 0, typeData, 16, 16); + Array.Copy(globalOffset.GetBytes(), 0, typeData, 32, 24); + typeData[56] = (byte)type; + + effect.Effect[0].TypeData = typeData; + + Client.Network.SendPacket(effect); + } + + /// + /// + /// + /// + /// + /// + /// + /// + public void BeamEffect(LLUUID sourceAvatar, LLUUID targetObject, LLVector3d globalOffset, LLColor color, + float duration) + { + ViewerEffectPacket effect = new ViewerEffectPacket(); + effect.Effect = new ViewerEffectPacket.EffectBlock[1]; + effect.Effect[0] = new ViewerEffectPacket.EffectBlock(); + effect.Effect[0].Color = color.GetBytes(); + effect.Effect[0].Duration = duration; + effect.Effect[0].ID = LLUUID.Random(); + effect.Effect[0].Type = (byte)EffectType.Beam; + + byte[] typeData = new byte[56]; + if (sourceAvatar != null) + Array.Copy(sourceAvatar.GetBytes(), 0, typeData, 0, 16); + if (targetObject != null) + Array.Copy(targetObject.GetBytes(), 0, typeData, 16, 16); + Array.Copy(globalOffset.GetBytes(), 0, typeData, 32, 24); + + effect.Effect[0].TypeData = typeData; + + Client.Network.SendPacket(effect); + } + + /// + /// + /// + public void LookAtEffect() + { + } + + /// + /// Synchronize the local profile and interests information to the server + /// + public void SetAvatarInformation() + { + // Basic profile properties + AvatarPropertiesUpdatePacket apup = new AvatarPropertiesUpdatePacket(); + + apup.AgentData.AgentID = this.ID; + apup.AgentData.SessionID = Client.Network.SessionID; + apup.PropertiesData.AboutText = Helpers.StringToField(this.ProfileProperties.AboutText); + apup.PropertiesData.AllowPublish = this.ProfileProperties.AllowPublish; + apup.PropertiesData.FLAboutText = Helpers.StringToField(this.ProfileProperties.FirstLifeText); + apup.PropertiesData.FLImageID = this.ProfileProperties.FirstLifeImage; + apup.PropertiesData.ImageID = this.ProfileProperties.ProfileImage; + apup.PropertiesData.MaturePublish = this.ProfileProperties.MaturePublish; + apup.PropertiesData.ProfileURL = Helpers.StringToField(this.ProfileProperties.ProfileURL); + + // Interests + AvatarInterestsUpdatePacket aiup = new AvatarInterestsUpdatePacket(); + + aiup.AgentData.AgentID = this.ID; + aiup.AgentData.SessionID = Client.Network.SessionID; + aiup.PropertiesData.LanguagesText = Helpers.StringToField(this.ProfileInterests.LanguagesText); + aiup.PropertiesData.SkillsMask = this.ProfileInterests.SkillsMask; + aiup.PropertiesData.SkillsText = Helpers.StringToField(this.ProfileInterests.SkillsText); + aiup.PropertiesData.WantToMask = this.ProfileInterests.WantToMask; + aiup.PropertiesData.WantToText = Helpers.StringToField(this.ProfileInterests.WantToText); + + //Send packets + Client.Network.SendPacket(apup); + Client.Network.SendPacket(aiup); + } + + /// + /// Send a chat message + /// + /// The Message you're sending out. + /// Channel number (0 would be default 'Say' message, other numbers + /// denote the equivalent of /# in normal client). + /// Chat Type, see above. + public void Chat(string message, int channel, ChatType type) + { + ChatFromViewerPacket chat = new ChatFromViewerPacket(); + chat.AgentData.AgentID = this.ID; + chat.AgentData.SessionID = Client.Network.SessionID; + chat.ChatData.Channel = channel; + chat.ChatData.Message = Helpers.StringToField(message); + chat.ChatData.Type = (byte)type; + + Client.Network.SendPacket(chat); + } + + /// + /// Set the height and the width of the client window. This is used + /// by the server to build a virtual camera frustum for our avatar + /// + /// New height of the viewer window + /// New width of the viewer window + public void SetHeightWidth(ushort height, ushort width) + { + AgentHeightWidthPacket heightwidth = new AgentHeightWidthPacket(); + heightwidth.AgentData.AgentID = Client.Network.AgentID; + heightwidth.AgentData.SessionID = Client.Network.SessionID; + heightwidth.AgentData.CircuitCode = Client.Network.CurrentSim.CircuitCode; + heightwidth.HeightWidthBlock.Height = height; + heightwidth.HeightWidthBlock.Width = width; + heightwidth.HeightWidthBlock.GenCounter = HeightWidthGenCounter++; + + Client.Network.SendPacket(heightwidth); + } + + /// + /// Sends a request to sit on the specified object + /// + /// LLUUID of the object to sit on + /// Sit at offset + public void RequestSit(LLUUID targetID, LLVector3 offset) + { + AgentRequestSitPacket requestSit = new AgentRequestSitPacket(); + requestSit.AgentData.AgentID = Client.Network.AgentID; + requestSit.AgentData.SessionID = Client.Network.SessionID; + requestSit.TargetObject.TargetID = targetID; + requestSit.TargetObject.Offset = offset; + Client.Network.SendPacket(requestSit); + } + + /// + /// Request the list of muted things for this avatar + /// + public void RequestMuteList() + { + MuteListRequestPacket mute = new MuteListRequestPacket(); + mute.AgentData.AgentID = Client.Network.AgentID; + mute.AgentData.SessionID = Client.Network.SessionID; + mute.MuteData.MuteCRC = 0; + + Client.Network.SendPacket(mute); + } + + /// + /// Request the current L$ balance + /// + public void RequestBalance() + { + MoneyBalanceRequestPacket money = new MoneyBalanceRequestPacket(); + money.AgentData.AgentID = Client.Network.AgentID; + money.AgentData.SessionID = Client.Network.SessionID; + money.MoneyData.TransactionID = LLUUID.Zero; + + Client.Network.SendPacket(money); + } + + /// + /// Set the always running toggle on the server + /// + /// Whether the avatar should always run or not + public void SetAlwaysRun(bool alwaysRun) + { + SetAlwaysRunPacket run = new SetAlwaysRunPacket(); + run.AgentData.AgentID = Client.Network.AgentID; + run.AgentData.SessionID = Client.Network.SessionID; + run.AgentData.AlwaysRun = alwaysRun; + + Client.Network.SendPacket(run); + } + + /// + /// Follows a call to RequestSit() to actually sit on the object + /// + public void Sit() + { + AgentSitPacket sit = new AgentSitPacket(); + sit.AgentData.AgentID = Client.Network.AgentID; + sit.AgentData.SessionID = Client.Network.SessionID; + Client.Network.SendPacket(sit); + } + + /// + /// Give Money to destination Avatar + /// + /// UUID of the Target Avatar + /// Amount in L$ + /// Reason (optional normally) + public void GiveMoney(LLUUID target, int amount, string description) + { + // 5001 - transaction type for av to av money transfers + if (amount > 0) + GiveMoney(target, amount, description, 5001); + else + Client.Log("Attempted to pay zero or negative value " + amount, Helpers.LogLevel.Warning); + } + + /// + /// Give Money to destionation Object or Avatar + /// + /// UUID of the Target Object/Avatar + /// Amount in L$ + /// Reason (Optional normally) + /// The type of transaction. Currently only 5001 is + /// documented for Av->Av money transfers. + public void GiveMoney(LLUUID target, int amount, string description, int transactiontype) + { + MoneyTransferRequestPacket money = new MoneyTransferRequestPacket(); + money.AgentData.AgentID = this.ID; + money.AgentData.SessionID = Client.Network.SessionID; + money.MoneyData.Description = Helpers.StringToField(description); + money.MoneyData.DestID = target; + money.MoneyData.SourceID = this.ID; + money.MoneyData.TransactionType = transactiontype; + money.MoneyData.AggregatePermInventory = 0; //TODO: whats this? + money.MoneyData.AggregatePermNextOwner = 0; //TODO: whats this? + money.MoneyData.Flags = 0; //TODO: whats this? + money.MoneyData.Amount = amount; + + Client.Network.SendPacket(money); + } + + /// + /// Send an AgentAnimation packet that toggles a single animation on + /// + /// The animation to start playing + public void AnimationStart(LLUUID animation) + { + Dictionary animations = new Dictionary(); + animations[animation] = true; + + Animate(animations); + } + + /// + /// Send an AgentAnimation packet that toggles a single animation off + /// + /// The animation to stop playing + public void AnimationStop(LLUUID animation) + { + Dictionary animations = new Dictionary(); + animations[animation] = false; + + Animate(animations); + } + + /// + /// Send an AgentAnimation packet that will toggle animations on or off + /// + /// A list of animation UUIDs, and whether to + /// turn that animation on or off + public void Animate(Dictionary animations) + { + AgentAnimationPacket animate = new AgentAnimationPacket(); + + animate.AgentData.AgentID = Client.Network.AgentID; + animate.AgentData.SessionID = Client.Network.SessionID; + animate.AnimationList = new AgentAnimationPacket.AnimationListBlock[animations.Count]; + int i = 0; + + foreach (KeyValuePair animation in animations) + { + animate.AnimationList[i] = new AgentAnimationPacket.AnimationListBlock(); + animate.AnimationList[i].AnimID = animation.Key; + animate.AnimationList[i].StartAnim = animation.Value; + + i++; + } + + Client.Network.SendPacket(animate); + } + + /// + /// Use the autopilot sim function to move the avatar to a new position + /// + /// The z value is currently not handled properly by the simulator + /// Integer value for the global X coordinate to move to + /// Integer value for the global Y coordinate to move to + /// Floating-point value for the Z coordinate to move to + /// AutoPilot(252620, 247078, 20.2674); + public void AutoPilot(ulong globalX, ulong globalY, float z) + { + GenericMessagePacket autopilot = new GenericMessagePacket(); + + autopilot.AgentData.AgentID = Client.Network.AgentID; + autopilot.AgentData.SessionID = Client.Network.SessionID; + autopilot.MethodData.Invoice = LLUUID.Zero; + autopilot.MethodData.Method = Helpers.StringToField("autopilot"); + autopilot.ParamList = new GenericMessagePacket.ParamListBlock[3]; + autopilot.ParamList[0] = new GenericMessagePacket.ParamListBlock(); + autopilot.ParamList[0].Parameter = Helpers.StringToField(globalX.ToString()); + autopilot.ParamList[1] = new GenericMessagePacket.ParamListBlock(); + autopilot.ParamList[1].Parameter = Helpers.StringToField(globalY.ToString()); + autopilot.ParamList[2] = new GenericMessagePacket.ParamListBlock(); + // TODO: Do we need to prevent z coordinates from being sent in 1.4827e-18 notation? + autopilot.ParamList[2].Parameter = Helpers.StringToField(z.ToString()); + + Client.Network.SendPacket(autopilot); + } + + /// + /// Use the autopilot sim function to move the avatar to a new position + /// + /// The z value is currently not handled properly by the simulator + /// Integer value for the local X coordinate to move to + /// Integer value for the local Y coordinate to move to + /// Floating-point value for the Z coordinate to move to + /// AutoPilot(252620, 247078, 20.2674); + public void AutoPilotLocal(int localX, int localY, float z) + { + uint x, y; + Helpers.LongToUInts(Client.Network.CurrentSim.Region.Handle, out x, out y); + AutoPilot((ulong)(x + localX), (ulong)(y + localY), z); + } + + /// + /// Start a teleport process + /// + /// + /// Position for Teleport + /// Callback ID + public void BeginTeleport(ulong regionHandle, LLVector3 position, TeleportCallback tc) + { + BeginTeleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z), tc); + } + + /// + /// Start a teleport process + /// + /// + /// Position for Teleport + /// Target to look at + /// Callback ID + public void BeginTeleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt, TeleportCallback tc) + { + OnBeginTeleport = tc; + + TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); + teleport.AgentData.AgentID = Client.Network.AgentID; + teleport.AgentData.SessionID = Client.Network.SessionID; + teleport.Info.LookAt = lookAt; + teleport.Info.Position = position; + teleport.Info.RegionHandle = regionHandle; + + Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); + + Client.Network.SendPacket(teleport); + } + + /// + /// Start a teleport process + /// + /// + /// Position for Teleport + /// + public bool Teleport(ulong regionHandle, LLVector3 position) + { + return Teleport(regionHandle, position, new LLVector3(position.X + 1.0f, position.Y, position.Z)); + } + + /// + /// Start a teleport process + /// + /// + /// Position for Teleport + /// Target to look at + /// + public bool Teleport(ulong regionHandle, LLVector3 position, LLVector3 lookAt) + { + TeleportStat = TeleportStatus.None; + + TeleportLocationRequestPacket teleport = new TeleportLocationRequestPacket(); + teleport.AgentData.AgentID = Client.Network.AgentID; + teleport.AgentData.SessionID = Client.Network.SessionID; + teleport.Info.LookAt = lookAt; + teleport.Info.Position = position; + + teleport.Info.RegionHandle = regionHandle; + + Client.Log("Teleporting to region " + regionHandle.ToString(), Helpers.LogLevel.Info); + + // Start the timeout check + TeleportTimeout = false; + TeleportTimer.Start(); + + Client.Network.SendPacket(teleport); + + // FIXME: Use a ManualResetEvent, Client.Tick() is bad + while (TeleportStat != TeleportStatus.Failed && TeleportStat != TeleportStatus.Finished && !TeleportTimeout) + { + Client.Tick(); + } + + TeleportTimer.Stop(); + + if (TeleportTimeout) + { + teleportMessage = "Teleport timed out."; + TeleportStat = TeleportStatus.Failed; + + if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); } + } + else + { + if (OnTeleport != null) { OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); } + } + + return (TeleportStat == TeleportStatus.Finished); + } + + /// + /// Generic Teleport Function + /// + /// Region name + /// Position for Teleport + /// + public bool Teleport(string simName, LLVector3 position) + { + //position.Z = 0; //why was this here? + return Teleport(simName, position, new LLVector3(0, 1.0F, 0)); + } + + /// + /// Teleport Function + /// + /// Region name + /// Position for Teleport + /// Target to look at + /// + public bool Teleport(string simName, LLVector3 position, LLVector3 lookAt) + { + int attempts = 0; + TeleportStat = TeleportStatus.None; + + simName = simName.ToLower(); + + GridRegion region = Client.Grid.GetGridRegion(simName); + + if (region != null) + { + return Teleport(region.RegionHandle, position, lookAt); + } + else + { + while (attempts++ < 5) + { + region = Client.Grid.GetGridRegion(simName); + + if (region != null) + { + return Teleport(region.RegionHandle, position, lookAt); + } + else + { + // Request the region info again + Client.Grid.AddSim(simName); + + // FIXME: We shouldn't be sleeping in the library at all, hopefully this goes away soon + System.Threading.Thread.Sleep(1000); + } + } + } + + if (OnTeleport != null) + { + teleportMessage = "Unable to resolve name: " + simName; + TeleportStat = TeleportStatus.Failed; + OnTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); + } + + return false; + } + + /// + /// Respond to a teleport lure by either accepting it and initiating + /// the teleport, or denying it + /// + /// UUID of the avatar requesting the teleport + /// Accept the teleport request or deny it + public void TeleportLureRespond(LLUUID requesterID, bool accept) + { + InstantMessage(requesterID, accept ? InstantMessageDialog.AcceptTeleport : InstantMessageDialog.DenyTeleport); + + if (accept) + { + TeleportLureRequestPacket lure = new TeleportLureRequestPacket(); + + lure.Info.AgentID = Client.Network.AgentID; + lure.Info.SessionID = Client.Network.SessionID; + lure.Info.LureID = Client.Network.AgentID; + lure.Info.TeleportFlags = 4; // TODO: What does this mean? + + Client.Network.SendPacket(lure); + } + } + + /// + /// Grabs an object + /// + /// Local ID of Object to grab + public void Grab(uint objectLocalID) + { + ObjectGrabPacket grab = new ObjectGrabPacket(); + grab.AgentData.AgentID = Client.Network.AgentID; + grab.AgentData.SessionID = Client.Network.SessionID; + grab.ObjectData.LocalID = objectLocalID; + grab.ObjectData.GrabOffset = new LLVector3(0, 0, 0); + Client.Network.SendPacket(grab); + } + + /// + /// Drags on an object + /// + /// Strangely, LLUID instead of local ID + /// Drag target in region coordinates + public void GrabUpdate(LLUUID objectID, LLVector3 grabPosition) + { + ObjectGrabUpdatePacket grab = new ObjectGrabUpdatePacket(); + grab.AgentData.AgentID = Client.Network.AgentID; + grab.AgentData.SessionID = Client.Network.SessionID; + grab.ObjectData.ObjectID = objectID; + grab.ObjectData.GrabOffsetInitial = new LLVector3(0, 0, 0); + grab.ObjectData.GrabPosition = grabPosition; + grab.ObjectData.TimeSinceLast = 0; + Client.Network.SendPacket(grab); + } + + /// + /// Releases a grabbed object + /// + public void DeGrab(uint objectLocalID) + { + ObjectDeGrabPacket degrab = new ObjectDeGrabPacket(); + degrab.AgentData.AgentID = Client.Network.AgentID; + degrab.AgentData.SessionID = Client.Network.SessionID; + degrab.ObjectData.LocalID = objectLocalID; + Client.Network.SendPacket(degrab); + } + + /// + /// Touches an object + /// + public void Touch(uint objectLocalID) + { + Client.Self.Grab(objectLocalID); + Client.Self.DeGrab(objectLocalID); + } + + /// + /// Request to join a group. If there is an enrollment fee it will + /// automatically be deducted from your balance + /// + /// The group to attempt to join + public void RequestJoinGroup(LLUUID groupID) + { + JoinGroupRequestPacket join = new JoinGroupRequestPacket(); + + join.AgentData.AgentID = Client.Network.AgentID; + join.AgentData.SessionID = Client.Network.SessionID; + join.GroupData.GroupID = groupID; + + Client.Network.SendPacket(join); + } + + /// + /// Request to leave a group + /// + /// The group to attempt to leave + public void RequestLeaveGroup(LLUUID groupID) + { + LeaveGroupRequestPacket leave = new LeaveGroupRequestPacket(); + + leave.AgentData.AgentID = Client.Network.AgentID; + leave.AgentData.SessionID = Client.Network.SessionID; + leave.GroupData.GroupID = groupID; + + Client.Network.SendPacket(leave); + } + + /// + /// Set our current active group + /// + /// The group we are a member of that we want to + /// activate + public void ActivateGroup(LLUUID groupID) + { + ActivateGroupPacket activate = new ActivateGroupPacket(); + + activate.AgentData.AgentID = Client.Network.AgentID; + activate.AgentData.SessionID = Client.Network.SessionID; + activate.AgentData.GroupID = groupID; + + Client.Network.SendPacket(activate); + } + + /// + /// Move an agent in to a simulator. This packet is the last packet + /// needed to complete the transition in to a new simulator + /// + /// + public void CompleteAgentMovement(Simulator simulator) + { + CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); + + move.AgentData.AgentID = Client.Network.AgentID; + move.AgentData.SessionID = Client.Network.SessionID; + move.AgentData.CircuitCode = simulator.CircuitCode; + + Client.Network.SendPacket(move, simulator); + } + + /// + /// Sends camera and action updates to the server including the + /// position and orientation of our camera, and a ControlFlags field + /// specifying our current movement actions + /// + /// Whether to ensure this packet makes it to the server + public void UpdateCamera(Avatar.AgentUpdateFlags controlFlags, LLVector3 position, LLVector3 forwardAxis, + LLVector3 leftAxis, LLVector3 upAxis, LLQuaternion bodyRotation, LLQuaternion headRotation, float farClip, + bool reliable) + { + AgentUpdatePacket update = new AgentUpdatePacket(); + + update.AgentData.AgentID = Client.Network.AgentID; + update.AgentData.SessionID = Client.Network.SessionID; + update.AgentData.State = 0; + update.AgentData.BodyRotation = bodyRotation; + update.AgentData.HeadRotation = headRotation; + update.AgentData.CameraCenter = position; + update.AgentData.CameraAtAxis = forwardAxis; + update.AgentData.CameraLeftAxis = leftAxis; + update.AgentData.CameraUpAxis = upAxis; + update.AgentData.Far = farClip; + update.AgentData.ControlFlags = (uint)controlFlags; + update.AgentData.Flags = 0; + update.Header.Reliable = reliable; + + Client.Network.SendPacket(update); + } + + /// + /// [UNUSED - for now] + /// + /// + /// + private void CoarseLocationHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.CoarseLocationUpdate) + { + CoarseLocationUpdatePacket p = (CoarseLocationUpdatePacket) packet; + + if (p.Index.You < 0 || p.Index.You >= p.Location.Length) return; + /* 1.5 and 6 represent a 50% fudge factor (hysteresis) -- bushing */ + if (Math.Abs(Position.X-p.Location[p.Index.You].X) > 1.5) Position.X=p.Location[p.Index.You].X; + if (Math.Abs(Position.Y-p.Location[p.Index.You].Y) > 1.5) Position.Y=p.Location[p.Index.You].Y; + if (Math.Abs(Position.Z-(p.Location[p.Index.You].Z*4)) > 6) Position.Z=p.Location[p.Index.You].Z*4; + } + } + + /// + /// Take an incoming ImprovedInstantMessage packet, auto-parse, and if + /// OnInstantMessage is defined call that with the appropriate arguments. + /// + /// Incoming ImprovedInstantMessagePacket + /// Unused + private void InstantMessageHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.ImprovedInstantMessage) + { + ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet; + + if (OnInstantMessage != null) + { + OnInstantMessage( + im.AgentData.AgentID + , Helpers.FieldToString(im.MessageBlock.FromAgentName), + im.MessageBlock.ToAgentID + , im.MessageBlock.ParentEstateID + , im.MessageBlock.RegionID + , im.MessageBlock.Position + , im.MessageBlock.Dialog + , im.MessageBlock.FromGroup + , im.MessageBlock.ID + , new DateTime(im.MessageBlock.Timestamp) + , Helpers.FieldToString(im.MessageBlock.Message) + , im.MessageBlock.Offline + , im.MessageBlock.BinaryBucket + ); + } + } + } + + /// + /// Take an incoming Chat packet, auto-parse, and if OnChat is defined call + /// that with the appropriate arguments. + /// + /// Incoming ChatFromSimulatorPacket + /// Unused + private void ChatHandler(Packet packet, Simulator simulator) + { + if (OnChat != null) + { + ChatFromSimulatorPacket chat = (ChatFromSimulatorPacket)packet; + + OnChat(Helpers.FieldToFilteredString(chat.ChatData.Message, new char()) + , chat.ChatData.Audible + , chat.ChatData.ChatType + , chat.ChatData.SourceType + , Helpers.FieldToString(chat.ChatData.FromName) + , chat.ChatData.SourceID + , chat.ChatData.OwnerID + , chat.ChatData.Position + ); + } + } + + /// + /// Used for parsing llDialog's + /// + /// Incoming ScriptDialog packet + /// Unused + private void ScriptDialogHandler(Packet packet, Simulator simulator) + { + if (OnScriptDialog != null) + { + ScriptDialogPacket dialog = (ScriptDialogPacket)packet; + List buttons = new List(); + + foreach (ScriptDialogPacket.ButtonsBlock button in dialog.Buttons) + { + buttons.Add(Helpers.FieldToString(button.ButtonLabel)); + } + + OnScriptDialog(Helpers.FieldToString(dialog.Data.Message), + Helpers.FieldToString(dialog.Data.ObjectName), + dialog.Data.ImageID, + dialog.Data.ObjectID, + Helpers.FieldToString(dialog.Data.FirstName), + Helpers.FieldToString(dialog.Data.LastName), + dialog.Data.ChatChannel, + buttons); + } + } + + /// + /// Update client's Position, LookAt and region handle from incoming packet + /// + /// Incoming AgentMovementCompletePacket + /// Unused + private void MovementCompleteHandler(Packet packet, Simulator simulator) + { + AgentMovementCompletePacket movement = (AgentMovementCompletePacket)packet; + + this.Position = movement.Data.Position; + this.LookAt = movement.Data.LookAt; + simulator.Region.Handle = movement.Data.RegionHandle; + } + + /// + /// Update Client Avatar's health via incoming packet + /// + /// Incoming HealthMessagePacket + /// Unused + private void HealthHandler(Packet packet, Simulator simulator) + { + health = ((HealthMessagePacket)packet).HealthData.Health; + } + + private void JoinGroupHandler(Packet packet, Simulator simulator) + { + if (OnJoinGroup != null) + { + JoinGroupReplyPacket reply = (JoinGroupReplyPacket)packet; + + OnJoinGroup(reply.GroupData.GroupID, reply.GroupData.Success); + } + } + + private void LeaveGroupHandler(Packet packet, Simulator simulator) + { + if (OnLeaveGroup != null) + { + LeaveGroupReplyPacket reply = (LeaveGroupReplyPacket)packet; + + OnLeaveGroup(reply.GroupData.GroupID, reply.GroupData.Success); + } + } + + public void AgentDataUpdateHandler(Packet packet, Simulator simulator) + { + AgentDataUpdatePacket p = (AgentDataUpdatePacket)packet; + if (p.AgentData.AgentID == simulator.Client.Network.AgentID) { + activeGroup = p.AgentData.ActiveGroupID; + } + } + + private void DropGroupHandler(Packet packet, Simulator simulator) + { + if (OnGroupDropped != null) + { + OnGroupDropped(((AgentDropGroupPacket)packet).AgentData.GroupID); + } + } + + /// + /// Update Client Avatar's L$ balance from incoming packet + /// + /// Incoming MoneyBalanceReplyPacket + /// Unused + private void BalanceHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.MoneyBalanceReply) + { + balance = ((MoneyBalanceReplyPacket)packet).MoneyData.MoneyBalance; + } + else if (packet.Type == PacketType.MoneySummaryReply) + { + balance = ((MoneySummaryReplyPacket)packet).MoneyData.Balance; + } + else if (packet.Type == PacketType.AdjustBalance) + { + balance += ((AdjustBalancePacket)packet).AgentData.Delta; + } + + if (OnBalanceUpdated != null) + { + OnBalanceUpdated(balance); + } + } + + private void EventQueueHandler(string message, object body) + { + if(message == "TeleportFinish") + { + Hashtable tpt = (Hashtable)body; + Hashtable info = (Hashtable)tpt["Info"]; + + // FIXME: quick and dirty hack + TeleportFinishPacket packet = new TeleportFinishPacket(); + + packet.Info.SimIP = Helpers.BytesToUInt((byte[])info["SimIP"]); + packet.Info.LocationID = Helpers.BytesToUInt((byte[])info["LocationID"]); + packet.Info.TeleportFlags = Helpers.BytesToUInt((byte[])info["TeleportFlags"]); + packet.Info.AgentID = (LLUUID)info["AgentID"]; + packet.Info.RegionHandle = Helpers.BytesToUInt64((byte[])info["RegionHandle"]); + packet.Info.SeedCapability = Helpers.StringToField((string)info["SeedCapability"]); + packet.Info.SimPort = (ushort)(long)info["SimPort"]; + packet.Info.SimAccess = (byte)(long)info["SimAccess"]; + + TeleportHandler(packet,Client.Network.CurrentSim); + } + } + + /// + /// Process an incoming effect + /// + private void ViewerEffectHandler(Packet packet, Simulator simulator) + { + ViewerEffectPacket effect = (ViewerEffectPacket)packet; + + foreach (ViewerEffectPacket.EffectBlock block in effect.Effect) + { + EffectType type; + + try + { + type = (EffectType)block.Type; + } + catch (Exception) + { + Client.Log("Received a ViewerEffect block with an unknown type " + block.Type, + Helpers.LogLevel.Warning); + continue; + } + + //LLColor color; + //if (block.Color.Length == 4) + //{ + // color = new LLColor(block.Color, 0); + //} + //else + //{ + // Client.Log("Received a ViewerEffect.EffectBlock.Color array with " + block.Color.Length + " bytes", + // Helpers.LogLevel.Warning); + // color = new LLColor(); + //} + + // Each ViewerEffect type uses it's own custom binary format for additional data. Fun eh? + switch (type) + { + case EffectType.Text: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Icon: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Connector: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.FlexibleObject: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.AnimalControls: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.AnimationObject: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Cloth: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Beam: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Glow: + Client.Log("Received a Glow ViewerEffect which is not implemented yet", + Helpers.LogLevel.Warning); + break; + case EffectType.Point: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Trail: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Sphere: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Spiral: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.Edit: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.LookAt: + Client.DebugLog("Received a ViewerEffect of type " + type.ToString() + ", implement me!"); + break; + case EffectType.PointAt: + if (block.TypeData.Length == 57) + { + LLUUID sourceAvatar = new LLUUID(block.TypeData, 0); + LLUUID targetObject = new LLUUID(block.TypeData, 16); + LLVector3d targetPos = new LLVector3d(block.TypeData, 32); + PointAtType pointAt; + try + { + pointAt = (PointAtType)block.TypeData[56]; + } + catch (Exception) + { + Client.Log("Unrecognized PointAtType " + block.TypeData[56], Helpers.LogLevel.Warning); + pointAt = PointAtType.Clear; + } + + // TODO: Create OnAvatarPointAt event and call it here + } + else + { + Client.Log("Received a PointAt ViewerEffect with an incorrect TypeData size of " + + block.TypeData.Length + " bytes", Helpers.LogLevel.Warning); + } + break; + } + } + } + + /// + /// Handler for teleport Requests + /// + /// Incoming TeleportHandler packet + /// Simulator sending teleport information + private void TeleportHandler(Packet packet, Simulator simulator) + { + if (packet.Type == PacketType.TeleportStart) + { + Client.DebugLog("TeleportStart received from " + simulator.ToString()); + + teleportMessage = "Teleport started"; + TeleportStat = TeleportStatus.Start; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); + } + } + else if (packet.Type == PacketType.TeleportProgress) + { + Client.DebugLog("TeleportProgress received from " + simulator.ToString()); + + teleportMessage = Helpers.FieldToString(((TeleportProgressPacket)packet).Info.Message); + TeleportStat = TeleportStatus.Progress; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); + } + } + else if (packet.Type == PacketType.TeleportFailed) + { + Client.DebugLog("TeleportFailed received from " + simulator.ToString()); + + teleportMessage = Helpers.FieldToString(((TeleportFailedPacket)packet).Info.Reason); + TeleportStat = TeleportStatus.Failed; + + if (OnBeginTeleport != null) + { + OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); + } + + OnBeginTeleport = null; + } + else if (packet.Type == PacketType.TeleportFinish) + { + Client.DebugLog("TeleportFinish received from " + simulator.ToString()); + + TeleportFinishPacket finish = (TeleportFinishPacket)packet; + Simulator previousSim = Client.Network.CurrentSim; + + // Connect to the new sim + String seedcaps = Encoding.UTF8.GetString(finish.Info.SeedCapability).Replace("\x00",""); + Simulator sim = Client.Network.Connect(new IPAddress((long)finish.Info.SimIP), finish.Info.SimPort, + simulator.CircuitCode, true, seedcaps); + + if (sim != null) + { + teleportMessage = "Teleport finished"; + TeleportStat = TeleportStatus.Finished; + + // Move the avatar in to the new sim + CompleteAgentMovementPacket move = new CompleteAgentMovementPacket(); + move.AgentData.AgentID = Client.Network.AgentID; + move.AgentData.SessionID = Client.Network.SessionID; + move.AgentData.CircuitCode = simulator.CircuitCode; + Client.Network.SendPacket(move, sim); + + // Disconnect from the previous sim + Client.Network.DisconnectSim(previousSim); + + Client.Log("Moved to new sim " + sim.ToString(), Helpers.LogLevel.Info); + + if (OnBeginTeleport != null) + { + OnBeginTeleport(sim, teleportMessage, TeleportStat); + } + else + { + // Sleep a little while so we can collect parcel information + // NOTE: This doesn't belong in libsecondlife + // System.Threading.Thread.Sleep(1000); + } + } + else + { + teleportMessage = "Failed to connect to the new sim after a teleport"; + TeleportStat = TeleportStatus.Failed; + + // FIXME: Set the previous CurrentSim to the current simulator again + + Client.Log(teleportMessage, Helpers.LogLevel.Warning); + + if (OnBeginTeleport != null) + { + OnBeginTeleport(Client.Network.CurrentSim, teleportMessage, TeleportStat); + } + } + + OnBeginTeleport = null; + } + } + + /// + /// Teleport Timer Event Handler. Used for enforcing timeouts. + /// + /// + /// + private void TeleportTimerEvent(object source, System.Timers.ElapsedEventArgs ea) + { + TeleportTimeout = true; + } + } +} diff --git a/libsecondlife-cs/examples/TestClient/ClientManager.cs b/libsecondlife-cs/examples/TestClient/ClientManager.cs index 6bb4af8c..2238070f 100644 --- a/libsecondlife-cs/examples/TestClient/ClientManager.cs +++ b/libsecondlife-cs/examples/TestClient/ClientManager.cs @@ -55,8 +55,9 @@ namespace libsecondlife.TestClient client.SimPrims = SimPrims; client.Master = account.Master; - bool login = client.Network.Login(account.FirstName, account.LastName, account.Password, - "TestClient", "contact@libsecondlife.org"); + if ( ! client.Network.Login(account.FirstName, account.LastName, account.Password, "TestClient", "contact@libsecondlife.org") ) { + Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + ": " + client.Network.LoginError); + } if (client.Network.Connected) { @@ -69,11 +70,6 @@ namespace libsecondlife.TestClient client.Throttle.Task = 1536000.0f; client.Throttle.Set(); } - else - { - Console.WriteLine("Failed to login " + account.FirstName + " " + account.LastName + - ": " + client.Network.LoginError); - } return client; } diff --git a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs index a1d1c397..2d3cd09e 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/ImportCommand.cs @@ -1,236 +1,241 @@ -using System; -using System.Collections.Generic; -using libsecondlife; -using libsecondlife.Packets; -using System.Xml; -using System.Threading; -using System.Xml.Serialization; -using System.IO; - -namespace libsecondlife.TestClient -{ - public class Linkset - { - public PrimObject RootPrim; - public List Children; - - public Linkset() - { - RootPrim = new PrimObject(); - Children = new List(); - } - - public Linkset(PrimObject rootPrim) - { - RootPrim = rootPrim; - Children = new List(); - } - } - - public class ImportCommand : Command - { - PrimObject currentPrim; - LLVector3 currentPosition; - SecondLife currentClient; - ManualResetEvent primDone; - List primsCreated; - uint rootLocalID = 0; - bool registeredCreateEvent = false; - bool rezzingRootPrim = false; - bool linking = false; - - public ImportCommand(TestClient testClient) - { - Name = "import"; - Description = "Import prims from an exported xml file. Usage: import [filename.xml]"; - primDone = new ManualResetEvent(false); - registeredCreateEvent = false; - } - - public override string Execute(string[] args, LLUUID fromAgentID) - { - if (args.Length != 1) - return "Usage: import inputfile.xml"; - - string name = args[0]; - Dictionary prims; - - currentClient = Client; - - try - { - XmlReader reader = XmlReader.Create(name); - List listprims = Helpers.PrimListFromXml(reader); - reader.Close(); - - // Create a dictionary indexed by the old local ID of the prims - prims = new Dictionary(); - foreach (PrimObject prim in listprims) - { - prims.Add(prim.LocalID, prim); - } - } - catch (Exception) - { - return "Failed to import the object XML file, maybe it doesn't exist or is in the wrong format?"; - } - - if (!registeredCreateEvent) - { - Client.OnPrimCreated += new TestClient.PrimCreatedCallback(TestClient_OnPrimCreated); - registeredCreateEvent = true; - } - - // Build an organized structure from the imported prims - Dictionary linksets = new Dictionary(); - foreach (PrimObject prim in prims.Values) - { - if (prim.ParentID == 0) - { - if (linksets.ContainsKey(prim.LocalID)) - linksets[prim.LocalID].RootPrim = prim; - else - linksets[prim.LocalID] = new Linkset(prim); - } - else - { - if (!linksets.ContainsKey(prim.ParentID)) - linksets[prim.ParentID] = new Linkset(); - - linksets[prim.ParentID].Children.Add(prim); - } - } - - primsCreated = new List(); - linking = false; - Console.WriteLine("Importing " + linksets.Count + " structures."); - - foreach (Linkset linkset in linksets.Values) - { - if (linkset.RootPrim.LocalID != 0) - { - // HACK: Offset the root prim position so it's not lying on top of the original - // We need a more elaborate solution for importing with relative or absolute offsets - linkset.RootPrim.Position.Z += 3.0f; - currentPosition = linkset.RootPrim.Position; - - // Rez the root prim with no rotation - LLQuaternion rootRotation = linkset.RootPrim.Rotation; - linkset.RootPrim.Rotation = LLQuaternion.Identity; - - rezzingRootPrim = true; - currentPrim = linkset.RootPrim; - - Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim, linkset.RootPrim.Position); - - if (!primDone.WaitOne(10000, false)) - return "Rez failed, timed out while creating a prim."; - primDone.Reset(); - - rezzingRootPrim = false; - - // Rez the child prims - foreach (PrimObject prim in linkset.Children) - { - currentPrim = prim; - currentPosition = prim.Position + linkset.RootPrim.Position; - - Client.Objects.AddPrim(Client.Network.CurrentSim, prim, currentPosition); - - if (!primDone.WaitOne(10000, false)) - return "Rez failed, timed out while creating a prim."; - primDone.Reset(); - } - - // Create a list of the local IDs of the newly created prims - List primIDs = new List(); - foreach (PrimObject prim in primsCreated) - { - if (prim.LocalID != rootLocalID) - primIDs.Add(prim.LocalID); - } - // Make sure the root object is the last in our list so it becomes the new root - primIDs.Add(rootLocalID); - - // Link and set the permissions + rotation - linking = true; - - Client.Objects.LinkPrims(Client.Network.CurrentSim, primIDs); - - Client.Objects.SetPermissions(Client.Network.CurrentSim, primIDs, - Helpers.PermissionWho.Everyone | Helpers.PermissionWho.Group | Helpers.PermissionWho.NextOwner, - Helpers.PermissionType.Copy | Helpers.PermissionType.Modify | Helpers.PermissionType.Move | - Helpers.PermissionType.Transfer, true); - - Client.Objects.SetRotation(Client.Network.CurrentSim, rootLocalID, rootRotation); - - for (int i = 0; i < linkset.Children.Count + 1; i++) - { - primDone.WaitOne(10000, false); - primDone.Reset(); - } - - linking = false; - } - else - { - // Skip linksets with a missing root prim - Console.WriteLine("WARNING: Skipping a linkset with a missing root prim"); - } - - // Reset everything for the next linkset - primsCreated.Clear(); - } - - return "Import complete."; - } - - void TestClient_OnPrimCreated(Simulator simulator, PrimObject prim) - { - if (rezzingRootPrim) - { - rootLocalID = prim.LocalID; - } - - if (!linking) - { - Console.WriteLine("Setting properties for " + prim.LocalID); - - primsCreated.Add(prim); - - // FIXME: Replace these individual calls with a single ObjectUpdate that sets the - // particle system and everything - currentClient.Objects.SetPosition(simulator, prim.LocalID, currentPosition); - currentClient.Objects.SetTextures(simulator, prim.LocalID, currentPrim.Textures); - //currentClient.Objects.SetLight(simulator, prim.LocalID, currentPrim.Light); - //currentClient.Objects.SetFlexible(simulator, prim.LocalID, currentPrim.Flexible); - } - - primDone.Set(); - } - - void OnUnknownAttribute(object obj, XmlAttributeEventArgs args) - { - // This hasn't happened for me - Console.WriteLine("OnUnknownAttribute: " + args.Attr.Name); - } - - void OnUnknownElement(object obj, XmlElementEventArgs args) - { - // Breakpoint here and look at the args class - Console.WriteLine(args.Element.Name); - } - - void OnUnknownNode(object obj, XmlNodeEventArgs args) - { - // Breakpoint here and look at the args class - Console.WriteLine(args.Name); - } - - void OnUnreferenced(object obj, UnreferencedObjectEventArgs args) - { - // This hasn't happened for me - Console.WriteLine("OnUnreferenced: " + args.UnreferencedObject.ToString()); - } - } +using System; +using System.Collections.Generic; +using libsecondlife; +using libsecondlife.Packets; +using System.Xml; +using System.Threading; +using System.Xml.Serialization; +using System.IO; + +namespace libsecondlife.TestClient +{ + public class Linkset + { + public PrimObject RootPrim; + public List Children; + + public Linkset() + { + RootPrim = new PrimObject(); + Children = new List(); + } + + public Linkset(PrimObject rootPrim) + { + RootPrim = rootPrim; + Children = new List(); + } + } + + public class ImportCommand : Command + { + PrimObject currentPrim; + LLVector3 currentPosition; + SecondLife currentClient; + ManualResetEvent primDone; + List primsCreated; + uint rootLocalID = 0; + bool registeredCreateEvent = false; + bool rezzingRootPrim = false; + bool linking = false; + + public ImportCommand(TestClient testClient) + { + Name = "import"; + Description = "Import prims from an exported xml file. Usage: import [filename.xml]"; + primDone = new ManualResetEvent(false); + registeredCreateEvent = false; + } + + public override string Execute(string[] args, LLUUID fromAgentID) + { + if (args.Length != 1) + return "Usage: import inputfile.xml"; + + string name = args[0]; + Dictionary prims; + + currentClient = Client; + + try + { + XmlReader reader = XmlReader.Create(name); + List listprims = Helpers.PrimListFromXml(reader); + reader.Close(); + + // Create a dictionary indexed by the old local ID of the prims + prims = new Dictionary(); + foreach (PrimObject prim in listprims) + { + prims.Add(prim.LocalID, prim); + } + } + catch (Exception) + { + return "Failed to import the object XML file, maybe it doesn't exist or is in the wrong format?"; + } + + if (!registeredCreateEvent) + { + Client.OnPrimCreated += new TestClient.PrimCreatedCallback(TestClient_OnPrimCreated); + registeredCreateEvent = true; + } + + // Build an organized structure from the imported prims + Dictionary linksets = new Dictionary(); + foreach (PrimObject prim in prims.Values) + { + if (prim.ParentID == 0) + { + if (linksets.ContainsKey(prim.LocalID)) + linksets[prim.LocalID].RootPrim = prim; + else + linksets[prim.LocalID] = new Linkset(prim); + } + else + { + if (!linksets.ContainsKey(prim.ParentID)) + linksets[prim.ParentID] = new Linkset(); + + linksets[prim.ParentID].Children.Add(prim); + } + } + + primsCreated = new List(); + linking = false; + Console.WriteLine("Importing " + linksets.Count + " structures."); + + foreach (Linkset linkset in linksets.Values) + { + if (linkset.RootPrim.LocalID != 0) + { + // HACK: Offset the root prim position so it's not lying on top of the original + // We need a more elaborate solution for importing with relative or absolute offsets + linkset.RootPrim.Position = Client.Self.Position; + linkset.RootPrim.Position.Z += 3.0f; + currentPosition = linkset.RootPrim.Position; + // A better solution would move the bot to the desired position. + // or to check if we are within a certain distance of the desired position. + + // Rez the root prim with no rotation + LLQuaternion rootRotation = linkset.RootPrim.Rotation; + linkset.RootPrim.Rotation = LLQuaternion.Identity; + + rezzingRootPrim = true; + currentPrim = linkset.RootPrim; + + Client.Objects.AddPrim(Client.Network.CurrentSim, linkset.RootPrim, linkset.RootPrim.Position); + + if (!primDone.WaitOne(10000, false)) + return "Rez failed, timed out while creating a prim."; + primDone.Reset(); + + rezzingRootPrim = false; + + // Rez the child prims + foreach (PrimObject prim in linkset.Children) + { + currentPrim = prim; + currentPosition = prim.Position + linkset.RootPrim.Position; + + Client.Objects.AddPrim(Client.Network.CurrentSim, prim, currentPosition); + + if (!primDone.WaitOne(10000, false)) + return "Rez failed, timed out while creating a prim."; + primDone.Reset(); + } + + // Create a list of the local IDs of the newly created prims + List primIDs = new List(); + foreach (PrimObject prim in primsCreated) + { + if (prim.LocalID != rootLocalID) + primIDs.Add(prim.LocalID); + } + // Make sure the root object is the last in our list so it becomes the new root + primIDs.Add(rootLocalID); + + // Link and set the permissions + rotation + linking = true; + + Client.Objects.LinkPrims(Client.Network.CurrentSim, primIDs); + + Client.Objects.SetPermissions(Client.Network.CurrentSim, primIDs, + Helpers.PermissionWho.Everyone | Helpers.PermissionWho.Group | Helpers.PermissionWho.NextOwner, + Helpers.PermissionType.Copy | Helpers.PermissionType.Modify | Helpers.PermissionType.Move | + Helpers.PermissionType.Transfer, true); + + Client.Objects.SetRotation(Client.Network.CurrentSim, rootLocalID, rootRotation); + + for (int i = 0; i < linkset.Children.Count + 1; i++) + { + primDone.WaitOne(10000, false); + primDone.Reset(); + } + + linking = false; + } + else + { + // Skip linksets with a missing root prim + Console.WriteLine("WARNING: Skipping a linkset with a missing root prim"); + } + + // Reset everything for the next linkset + primsCreated.Clear(); + } + + return "Import complete."; + } + + void TestClient_OnPrimCreated(Simulator simulator, PrimObject prim) + { + if (rezzingRootPrim) + { + rootLocalID = prim.LocalID; + } + + if (!linking) + { + Console.WriteLine("Setting properties for " + prim.LocalID); + + primsCreated.Add(prim); + + // FIXME: Replace these individual calls with a single ObjectUpdate that sets the + // particle system and everything + currentClient.Objects.SetPosition(simulator, prim.LocalID, currentPosition); + currentClient.Objects.SetTextures(simulator, prim.LocalID, currentPrim.Textures); + //currentClient.Objects.SetLight(simulator, prim.LocalID, currentPrim.Light); + //currentClient.Objects.SetFlexible(simulator, prim.LocalID, currentPrim.Flexible); + } + + primDone.Set(); + } + + /* It's not like these were being used + void OnUnknownAttribute(object obj, XmlAttributeEventArgs args) + { + // This hasn't happened for me + Console.WriteLine("OnUnknownAttribute: " + args.Attr.Name); + } + + void OnUnknownElement(object obj, XmlElementEventArgs args) + { + // Breakpoint here and look at the args class + Console.WriteLine(args.Element.Name); + } + + void OnUnknownNode(object obj, XmlNodeEventArgs args) + { + // Breakpoint here and look at the args class + Console.WriteLine(args.Name); + } + + void OnUnreferenced(object obj, UnreferencedObjectEventArgs args) + { + // This hasn't happened for me + Console.WriteLine("OnUnreferenced: " + args.UnreferencedObject.ToString()); + } + */ + } } \ No newline at end of file diff --git a/libsecondlife-cs/examples/TestClient/Commands/Movement/FollowCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/Movement/FollowCommand.cs index a4d3c35a..846d1167 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/Movement/FollowCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/Movement/FollowCommand.cs @@ -1,78 +1,76 @@ -using System; -using System.Collections.Generic; -using System.Text; -using libsecondlife; -using libsecondlife.Packets; - -namespace libsecondlife.TestClient -{ - public class FollowCommand: Command - { - public FollowCommand(TestClient testClient) - { - Name = "follow"; - Description = "Follow another avatar. (usage: follow [FirstName LastName]) If no target is set then will follow master."; - } - - public override string Execute(string[] args, LLUUID fromAgentID) - { - string target = String.Empty; - for (int ct = 0; ct < args.Length; ct++) - target = target + args[ct] + " "; - target = target.TrimEnd(); - - if (target.Length == 0) - target = Client.Master; - - if (target.Length > 0) - { - if (Follow(target)) - return "Following " + target; - else - return "Unable to follow " + target + ". Client may not be able to see that avatar."; - } - else - { - return "No target specified and no master is set. usage: follow [FirstName LastName])"; - } - } - - const float DISTANCE_BUFFER = 3.0f; - string followName; - Avatar followAvatar; - - bool Follow(string name) - { - foreach (Avatar av in Client.AvatarList.Values) - { - if (av.Name == name) - { - followName = name; - followAvatar = av; - Active = true; - return true; - } - } - return false; - } - - public override void Think() - { - if (Helpers.VecDist(followAvatar.Position, Client.Self.Position) > DISTANCE_BUFFER) - { - //move toward target - LLVector3 avPos = followAvatar.Position; - Client.Self.AutoPilot((ulong)avPos.X + (ulong)Client.regionX, (ulong)avPos.Y + (ulong)Client.regionY, avPos.Z); - } - //else - //{ - // //stop at current position - // LLVector3 myPos = client.Self.Position; - // client.Self.AutoPilot((ulong)myPos.x, (ulong)myPos.y, myPos.Z); - //} - - base.Think(); - } - - } -} +using System; +using System.Collections.Generic; +using System.Text; +using libsecondlife; +using libsecondlife.Packets; + +namespace libsecondlife.TestClient +{ + public class FollowCommand: Command + { + public FollowCommand(TestClient testClient) + { + Name = "follow"; + Description = "Follow another avatar. (usage: follow [FirstName LastName]) If no target is set then will follow master."; + } + + public override string Execute(string[] args, LLUUID fromAgentID) + { + string target = String.Empty; + for (int ct = 0; ct < args.Length; ct++) + target = target + args[ct] + " "; + target = target.TrimEnd(); + + if (target.Length == 0) + target = Client.Master; + + if (target.Length > 0) + { + if (Follow(target)) + return "Following " + target; + else + return "Unable to follow " + target + ". Client may not be able to see that avatar."; + } + else + { + return "No target specified and no master is set. usage: follow [FirstName LastName])"; + } + } + + const float DISTANCE_BUFFER = 3.0f; + Avatar followAvatar; + + bool Follow(string name) + { + foreach (Avatar av in Client.AvatarList.Values) + { + if (av.Name == name) + { + followAvatar = av; + Active = true; + return true; + } + } + return false; + } + + public override void Think() + { + if (Helpers.VecDist(followAvatar.Position, Client.Self.Position) > DISTANCE_BUFFER) + { + //move toward target + LLVector3 avPos = followAvatar.Position; + Client.Self.AutoPilot((ulong)avPos.X + (ulong)Client.regionX, (ulong)avPos.Y + (ulong)Client.regionY, avPos.Z); + } + //else + //{ + // //stop at current position + // LLVector3 myPos = client.Self.Position; + // client.Self.AutoPilot((ulong)myPos.x, (ulong)myPos.y, myPos.Z); + //} + + base.Think(); + } + + } +} diff --git a/libsecondlife-cs/examples/TestClient/Commands/Movement/MoveToCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/Movement/MoveToCommand.cs index d6a545f2..2b3c7d73 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/Movement/MoveToCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/Movement/MoveToCommand.cs @@ -11,7 +11,6 @@ namespace libsecondlife.TestClient.Commands.Movement { public override string Execute(string[] args, LLUUID fromAgentID) { if (args.Length != 3) return "usage: moveto x y z"; - Region curRegion = Client.Network.CurrentSim.Region; float x = Client.Self.Position.X + Client.regionX; float y = Client.Self.Position.Y + Client.regionY; float z = Client.Self.Position.Z; diff --git a/libsecondlife-cs/examples/TestClient/Commands/Movement/StandCommand.cs b/libsecondlife-cs/examples/TestClient/Commands/Movement/StandCommand.cs index 2eec45ef..7fde64e3 100644 --- a/libsecondlife-cs/examples/TestClient/Commands/Movement/StandCommand.cs +++ b/libsecondlife-cs/examples/TestClient/Commands/Movement/StandCommand.cs @@ -31,7 +31,7 @@ namespace libsecondlife.TestClient { AgentUpdatePacket p = new AgentUpdatePacket(); p.AgentData.Far = DRAW_DISTANCE; - LLVector3 myPos = client.Self.Position; + //LLVector3 myPos = client.Self.Position; p.AgentData.CameraCenter = new LLVector3(0, 0, 0); p.AgentData.CameraAtAxis = new LLVector3(0, 0, 0); p.AgentData.CameraLeftAxis = new LLVector3(0, 0, 0);