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);