/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org 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.Threading; using OpenMetaverse; using OpenMetaverse.Packets; namespace OpenMetaverse { public partial class AgentManager { #region Enums /// /// Used to specify movement actions for your agent /// [Flags] public enum ControlFlags { /// Empty flag NONE = 0, /// Move Forward (SL Keybinding: W/Up Arrow) AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX, /// Move Backward (SL Keybinding: S/Down Arrow) AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX, /// Move Left (SL Keybinding: Shift-(A/Left Arrow)) AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX, /// Move Right (SL Keybinding: Shift-(D/Right Arrow)) AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX, /// Not Flying: Jump/Flying: Move Up (SL Keybinding: E) AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX, /// Not Flying: Croutch/Flying: Move Down (SL Keybinding: C) AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX, /// Unused AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX, /// Unused AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX, /// Unused AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX, /// Unused AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX, /// ORed with AGENT_CONTROL_AT_* if the keyboard is being used AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX, /// ORed with AGENT_CONTROL_LEFT_* if the keyboard is being used AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX, /// ORed with AGENT_CONTROL_UP_* if the keyboard is being used AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX, /// Fly AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX, /// AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX, /// Finish our current animation AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX, /// Stand up from the ground or a prim seat AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX, /// Sit on the ground at our current location AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX, /// Whether mouselook is currently enabled AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX, /// Legacy, used if a key was pressed for less than a certain amount of time AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX, /// AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX, /// AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX, /// Set when the avatar is idled or set to away. Note that the away animation is /// activated separately from setting this flag AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX, /// AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX, /// AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX, /// AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX, /// AGENT_CONTROL_ML_LBUTTON_UP = 0x1 << CONTROL_ML_LBUTTON_UP_INDEX } #endregion Enums #region AgentUpdate Constants private const int CONTROL_AT_POS_INDEX = 0; private const int CONTROL_AT_NEG_INDEX = 1; private const int CONTROL_LEFT_POS_INDEX = 2; private const int CONTROL_LEFT_NEG_INDEX = 3; private const int CONTROL_UP_POS_INDEX = 4; private const int CONTROL_UP_NEG_INDEX = 5; private const int CONTROL_PITCH_POS_INDEX = 6; private const int CONTROL_PITCH_NEG_INDEX = 7; private const int CONTROL_YAW_POS_INDEX = 8; private const int CONTROL_YAW_NEG_INDEX = 9; private const int CONTROL_FAST_AT_INDEX = 10; private const int CONTROL_FAST_LEFT_INDEX = 11; private const int CONTROL_FAST_UP_INDEX = 12; private const int CONTROL_FLY_INDEX = 13; private const int CONTROL_STOP_INDEX = 14; private const int CONTROL_FINISH_ANIM_INDEX = 15; private const int CONTROL_STAND_UP_INDEX = 16; private const int CONTROL_SIT_ON_GROUND_INDEX = 17; private const int CONTROL_MOUSELOOK_INDEX = 18; private const int CONTROL_NUDGE_AT_POS_INDEX = 19; private const int CONTROL_NUDGE_AT_NEG_INDEX = 20; private const int CONTROL_NUDGE_LEFT_POS_INDEX = 21; private const int CONTROL_NUDGE_LEFT_NEG_INDEX = 22; private const int CONTROL_NUDGE_UP_POS_INDEX = 23; private const int CONTROL_NUDGE_UP_NEG_INDEX = 24; private const int CONTROL_TURN_LEFT_INDEX = 25; private const int CONTROL_TURN_RIGHT_INDEX = 26; private const int CONTROL_AWAY_INDEX = 27; private const int CONTROL_LBUTTON_DOWN_INDEX = 28; private const int CONTROL_LBUTTON_UP_INDEX = 29; private const int CONTROL_ML_LBUTTON_DOWN_INDEX = 30; private const int CONTROL_ML_LBUTTON_UP_INDEX = 31; private const int TOTAL_CONTROLS = 32; #endregion AgentUpdate Constants /// /// Agent movement and camera control /// /// Agent movement is controlled by setting specific /// After the control flags are set, An AgentUpdate is required to update the simulator of the specified flags /// This is most easily accomplished by setting one or more of the AgentMovement properties /// /// Movement of an avatar is always based on a compass direction, for example AtPos will move the /// agent from West to East or forward on the X Axis, AtNeg will of course move agent from /// East to West or backward on the X Axis, LeftPos will be South to North or forward on the Y Axis /// The Z axis is Up, finer grained control of movements can be done using the Nudge properties /// public partial class AgentMovement { #region Properties /// Move agent positive along the X axis public bool AtPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_POS, value); } } /// Move agent negative along the X axis public bool AtNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG, value); } } /// Move agent positive along the Y axis public bool LeftPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS, value); } } /// Move agent negative along the Y axis public bool LeftNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG, value); } } /// Move agent positive along the Z axis public bool UpPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_POS, value); } } /// Move agent negative along the Z axis public bool UpNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG, value); } } /// public bool PitchPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_POS, value); } } /// public bool PitchNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_PITCH_NEG, value); } } /// public bool YawPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS, value); } } /// public bool YawNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG, value); } } /// public bool FastAt { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_AT); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_AT, value); } } /// public bool FastLeft { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_LEFT); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_LEFT, value); } } /// public bool FastUp { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_UP); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FAST_UP, value); } } /// Causes simulator to make agent fly public bool Fly { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FLY); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FLY, value); } } /// Stop movement public bool Stop { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STOP); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STOP, value); } } /// Finish animation public bool FinishAnim { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FINISH_ANIM); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_FINISH_ANIM, value); } } /// Stand up from a sit public bool StandUp { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP, value); } } /// Tells simulator to sit agent on ground public bool SitOnGround { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND, value); } } /// Place agent into mouselook mode public bool Mouselook { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK, value); } } /// Nudge agent positive along the X axis public bool NudgeAtPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_POS, value); } } /// Nudge agent negative along the X axis public bool NudgeAtNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_AT_NEG, value); } } /// Nudge agent positive along the Y axis public bool NudgeLeftPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_POS, value); } } /// Nudge agent negative along the Y axis public bool NudgeLeftNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_LEFT_NEG, value); } } /// Nudge agent positive along the Z axis public bool NudgeUpPos { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_POS, value); } } /// Nudge agent negative along the Z axis public bool NudgeUpNeg { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG, value); } } /// public bool TurnLeft { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT, value); } } /// public bool TurnRight { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT, value); } } /// Tell simulator to mark agent as away public bool Away { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AWAY); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_AWAY, value); } } /// public bool LButtonDown { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN, value); } } /// public bool LButtonUp { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_UP, value); } } /// public bool MLButtonDown { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_DOWN, value); } } /// public bool MLButtonUp { get { return GetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_UP); } set { SetControlFlag(AgentManager.ControlFlags.AGENT_CONTROL_ML_LBUTTON_UP, value); } } /// /// Returns "always run" value, or changes it by sending a SetAlwaysRunPacket /// public bool AlwaysRun { get { return alwaysRun; } set { alwaysRun = value; SetAlwaysRunPacket run = new SetAlwaysRunPacket(); run.AgentData.AgentID = Client.Self.AgentID; run.AgentData.SessionID = Client.Self.SessionID; run.AgentData.AlwaysRun = alwaysRun; Client.Network.SendPacket(run); } } /// The current value of the agent control flags public uint AgentControls { get { return agentControls; } } /// Gets or sets the interval in milliseconds at which /// AgentUpdate packets are sent to the current simulator. Setting /// this to a non-zero value will also enable the packet sending if /// it was previously off, and setting it to zero will disable public int UpdateInterval { get { return updateInterval; } set { if (value > 0) { if (updateTimer != null) { updateTimer.Change(value, value); } updateInterval = value; } else { if (updateTimer != null) { updateTimer.Change(Timeout.Infinite, Timeout.Infinite); } updateInterval = 0; } } } /// Gets or sets whether AgentUpdate packets are sent to /// the current simulator public bool UpdateEnabled { get { return (updateInterval != 0); } } /// Reset movement controls every time we send an update public bool AutoResetControls { get { return autoResetControls; } set { autoResetControls = value; } } #endregion Properties /// Agent camera controls public AgentCamera Camera; /// Currently only used for hiding your group title public AgentFlags Flags = AgentFlags.None; /// Action state of the avatar, which can currently be /// typing and editing public AgentState State = AgentState.None; /// public Quaternion BodyRotation = Quaternion.Identity; /// public Quaternion HeadRotation = Quaternion.Identity; #region Change tracking /// private Quaternion LastBodyRotation; /// private Quaternion LastHeadRotation; /// private Vector3 LastCameraCenter; /// private Vector3 LastCameraXAxis; /// private Vector3 LastCameraYAxis; /// private Vector3 LastCameraZAxis; /// private float LastFar; #endregion Change tracking private bool alwaysRun; private GridClient Client; private uint agentControls; private int duplicateCount; private AgentState lastState; /// Timer for sending AgentUpdate packets private Timer updateTimer; private int updateInterval; private bool autoResetControls; /// Default constructor public AgentMovement(GridClient client) { Client = client; Camera = new AgentCamera(); Client.Network.LoginProgress += Network_OnConnected; Client.Network.Disconnected += Network_OnDisconnected; updateInterval = Settings.DEFAULT_AGENT_UPDATE_INTERVAL; } private void CleanupTimer() { if (updateTimer != null) { updateTimer.Dispose(); updateTimer = null; } } private void Network_OnDisconnected(object sender, DisconnectedEventArgs e) { CleanupTimer(); } private void Network_OnConnected(object sender, LoginProgressEventArgs e) { if (e.Status == LoginStatus.Success) { CleanupTimer(); updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed), null, updateInterval, updateInterval); } } /// /// Send an AgentUpdate with the camera set at the current agent /// position and pointing towards the heading specified /// /// Camera rotation in radians /// Whether to send the AgentUpdate reliable /// or not public void UpdateFromHeading(double heading, bool reliable) { Camera.Position = Client.Self.SimPosition; Camera.LookDirection(heading); BodyRotation.Z = (float)Math.Sin(heading / 2.0d); BodyRotation.W = (float)Math.Cos(heading / 2.0d); HeadRotation = BodyRotation; SendUpdate(reliable); } /// /// Rotates the avatar body and camera toward a target position. /// This will also anchor the camera position on the avatar /// /// Region coordinates to turn toward public bool TurnToward(Vector3 target) { return TurnToward(target, true); } /// /// Rotates the avatar body and camera toward a target position. /// This will also anchor the camera position on the avatar /// /// Region coordinates to turn toward /// whether to send update or not public bool TurnToward(Vector3 target, bool sendUpdate) { if (Client.Settings.SEND_AGENT_UPDATES) { Quaternion parentRot = Quaternion.Identity; if (Client.Self.SittingOn > 0) { if (!Client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(Client.Self.SittingOn)) { Logger.Log("Attempted TurnToward but parent prim is not in dictionary", Helpers.LogLevel.Warning, Client); return false; } else parentRot = Client.Network.CurrentSim.ObjectsPrimitives[Client.Self.SittingOn].Rotation; } Quaternion between = Vector3.RotationBetween(Vector3.UnitX, Vector3.Normalize(target - Client.Self.SimPosition)); Quaternion rot = between * (Quaternion.Identity / parentRot); BodyRotation = rot; HeadRotation = rot; Camera.LookAt(Client.Self.SimPosition, target); if (sendUpdate) SendUpdate(); return true; } else { Logger.Log("Attempted TurnToward but agent updates are disabled", Helpers.LogLevel.Warning, Client); return false; } } /// /// Send new AgentUpdate packet to update our current camera /// position and rotation /// public void SendUpdate() { SendUpdate(false, Client.Network.CurrentSim); } /// /// Send new AgentUpdate packet to update our current camera /// position and rotation /// /// Whether to require server acknowledgement /// of this packet public void SendUpdate(bool reliable) { SendUpdate(reliable, Client.Network.CurrentSim); } /// /// Send new AgentUpdate packet to update our current camera /// position and rotation /// /// Whether to require server acknowledgement /// of this packet /// Simulator to send the update to public void SendUpdate(bool reliable, Simulator simulator) { // Since version 1.40.4 of the Linden simulator, sending this update // causes corruption of the agent position in the simulator if (simulator != null && (!simulator.AgentMovementComplete)) return; Vector3 origin = Camera.Position; Vector3 xAxis = Camera.LeftAxis; Vector3 yAxis = Camera.AtAxis; Vector3 zAxis = Camera.UpAxis; // Attempted to sort these in a rough order of how often they might change if (agentControls == 0 && yAxis == LastCameraYAxis && origin == LastCameraCenter && State == lastState && HeadRotation == LastHeadRotation && BodyRotation == LastBodyRotation && xAxis == LastCameraXAxis && Camera.Far == LastFar && zAxis == LastCameraZAxis) { ++duplicateCount; } else { duplicateCount = 0; } if (Client.Settings.DISABLE_AGENT_UPDATE_DUPLICATE_CHECK || duplicateCount < 10) { // Store the current state to do duplicate checking LastHeadRotation = HeadRotation; LastBodyRotation = BodyRotation; LastCameraYAxis = yAxis; LastCameraCenter = origin; LastCameraXAxis = xAxis; LastCameraZAxis = zAxis; LastFar = Camera.Far; lastState = State; // Build the AgentUpdate packet and send it AgentUpdatePacket update = new AgentUpdatePacket(); update.Header.Reliable = reliable; update.AgentData.AgentID = Client.Self.AgentID; update.AgentData.SessionID = Client.Self.SessionID; update.AgentData.HeadRotation = HeadRotation; update.AgentData.BodyRotation = BodyRotation; update.AgentData.CameraAtAxis = xAxis; update.AgentData.CameraCenter = origin; update.AgentData.CameraLeftAxis = yAxis; update.AgentData.CameraUpAxis = zAxis; update.AgentData.Far = Camera.Far; update.AgentData.State = (byte)State; update.AgentData.ControlFlags = agentControls; update.AgentData.Flags = (byte)Flags; Client.Network.SendPacket(update, simulator); if (autoResetControls) { ResetControlFlags(); } } } /// /// Builds an AgentUpdate packet entirely from parameters. This /// will not touch the state of Self.Movement or /// Self.Movement.Camera in any way /// /// /// /// /// /// /// /// /// /// /// /// public void SendManualUpdate(AgentManager.ControlFlags controlFlags, Vector3 position, Vector3 forwardAxis, Vector3 leftAxis, Vector3 upAxis, Quaternion bodyRotation, Quaternion headRotation, float farClip, AgentFlags flags, AgentState state, bool reliable) { // Since version 1.40.4 of the Linden simulator, sending this update // causes corruption of the agent position in the simulator if (Client.Network.CurrentSim != null && (!Client.Network.CurrentSim.HandshakeComplete)) return; AgentUpdatePacket update = new AgentUpdatePacket(); update.AgentData.AgentID = Client.Self.AgentID; update.AgentData.SessionID = Client.Self.SessionID; 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 = (byte)flags; update.AgentData.State = (byte)state; update.Header.Reliable = reliable; Client.Network.SendPacket(update); } private bool GetControlFlag(ControlFlags flag) { return (agentControls & (uint)flag) != 0; } private void SetControlFlag(ControlFlags flag, bool value) { if (value) agentControls |= (uint)flag; else agentControls &= ~((uint)flag); } public void ResetControlFlags() { // Reset all of the flags except for persistent settings like // away, fly, mouselook, and crouching agentControls &= (uint)(ControlFlags.AGENT_CONTROL_AWAY | ControlFlags.AGENT_CONTROL_FLY | ControlFlags.AGENT_CONTROL_MOUSELOOK | ControlFlags.AGENT_CONTROL_UP_NEG); } /// /// Sends update of Field of Vision vertical angle to the simulator /// /// Angle in radians public void SetFOVVerticalAngle(float angle) { OpenMetaverse.Packets.AgentFOVPacket msg = new OpenMetaverse.Packets.AgentFOVPacket(); msg.AgentData.AgentID = Client.Self.AgentID; msg.AgentData.SessionID = Client.Self.SessionID; msg.AgentData.CircuitCode = Client.Network.CircuitCode; msg.FOVBlock.GenCounter = 0; msg.FOVBlock.VerticalAngle = angle; Client.Network.SendPacket(msg); } private void UpdateTimer_Elapsed(object obj) { if (Client.Network.Connected && Client.Settings.SEND_AGENT_UPDATES) { //Send an AgentUpdate packet SendUpdate(false, Client.Network.CurrentSim); } } } } }