From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 2595 +++++++++++++++------- 1 file changed, 1822 insertions(+), 773 deletions(-) (limited to 'OpenSim/Region/Framework/Scenes/ScenePresence.cs') diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index a90872e..1fddd91 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -29,16 +29,19 @@ using System; using System.Xml; using System.Collections.Generic; using System.Reflection; +using System.Threading; using System.Timers; +using Timer = System.Timers.Timer; using OpenMetaverse; using log4net; using Nini.Config; using OpenSim.Framework; using OpenSim.Framework.Client; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes.Animation; using OpenSim.Region.Framework.Scenes.Types; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.PhysicsModules.SharedBase; using GridRegion = OpenSim.Services.Interfaces.GridRegion; using OpenSim.Services.Interfaces; using TeleportFlags = OpenSim.Framework.Constants.TeleportFlags; @@ -63,6 +66,7 @@ namespace OpenSim.Region.Framework.Scenes struct ScriptControllers { + public UUID objectID; public UUID itemID; public ScriptControlled ignoreControls; public ScriptControlled eventControls; @@ -72,21 +76,51 @@ namespace OpenSim.Region.Framework.Scenes public class ScenePresence : EntityBase, IScenePresence { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly String LogHeader = "[SCENE PRESENCE]"; + // ~ScenePresence() // { // m_log.DebugFormat("[SCENE PRESENCE]: Destructor called on {0}", Name); // } - private void TriggerScenePresenceUpdated() + public void TriggerScenePresenceUpdated() { if (m_scene != null) m_scene.EventManager.TriggerScenePresenceUpdated(this); } - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public PresenceType PresenceType { get; private set; } + private ScenePresenceStateMachine m_stateMachine; + + /// + /// The current state of this presence. Governs only the existence lifecycle. See ScenePresenceStateMachine + /// for more details. + /// + public ScenePresenceState LifecycleState + { + get + { + return m_stateMachine.GetState(); + } + + set + { + m_stateMachine.SetState(value); + } + } + + /// + /// This exists to prevent race conditions between two CompleteMovement threads if the simulator is slow and + /// the viewer fires these in quick succession. + /// + /// + /// TODO: The child -> agent transition should be folded into LifecycleState and the CompleteMovement + /// regulation done there. + /// + private object m_completeMovementLock = new object(); + // private static readonly byte[] DEFAULT_TEXTURE = AvatarAppearance.GetDefaultTexture().GetBytes(); private static readonly Array DIR_CONTROL_FLAGS = Enum.GetValues(typeof(Dir_ControlFlags)); private static readonly Vector3 HEAD_ADJUSTMENT = new Vector3(0f, 0f, 0.3f); @@ -99,7 +133,7 @@ namespace OpenSim.Region.Framework.Scenes /// rotation, prim cut, prim twist, prim taper, and prim shear. See mantis /// issue #1716 /// - public static readonly Vector3 SIT_TARGET_ADJUSTMENT = new Vector3(0.0f, 0.0f, 0.418f); + public static readonly Vector3 SIT_TARGET_ADJUSTMENT = new Vector3(0.0f, 0.0f, 0.4f); /// /// Movement updates for agents in neighboring regions are sent directly to clients. @@ -139,6 +173,10 @@ namespace OpenSim.Region.Framework.Scenes private Vector3 m_lastPosition; private Quaternion m_lastRotation; private Vector3 m_lastVelocity; + private Vector3 m_lastSize = new Vector3(0.45f,0.6f,1.9f); + + private bool m_followCamAuto = false; + private Vector3? m_forceToApply; private int m_userFlags; @@ -196,10 +234,15 @@ namespace OpenSim.Region.Framework.Scenes private float m_sitAvatarHeight = 2.0f; private Vector3 m_lastChildAgentUpdatePosition; - private Vector3 m_lastChildAgentUpdateCamPosition; +// private Vector3 m_lastChildAgentUpdateCamPosition; private const int LAND_VELOCITYMAG_MAX = 12; + private const float FLY_ROLL_MAX_RADIANS = 1.1f; + + private const float FLY_ROLL_RADIANS_PER_UPDATE = 0.06f; + private const float FLY_ROLL_RESET_RADIANS_PER_UPDATE = 0.02f; + private float m_health = 100f; protected ulong crossingFromRegion; @@ -221,8 +264,6 @@ namespace OpenSim.Region.Framework.Scenes /// public bool LandAtTarget { get; private set; } - private bool m_followCamAuto; - private int m_movementUpdateCount; private const int NumMovementsBetweenRayCast = 5; @@ -230,6 +271,10 @@ namespace OpenSim.Region.Framework.Scenes //private int m_moveToPositionStateStatus; //***************************************************** + private int m_movementAnimationUpdateCounter = 0; + + public Vector3 PrevSitOffset { get; set; } + protected AvatarAppearance m_appearance; public AvatarAppearance Appearance @@ -242,6 +287,8 @@ namespace OpenSim.Region.Framework.Scenes } } + public bool SentInitialDataToClient { get; private set; } + /// /// Copy of the script states while the agent is in transit. This state may /// need to be placed back in case of transfer fail. @@ -276,9 +323,43 @@ namespace OpenSim.Region.Framework.Scenes /// private Vector3 posLastSignificantMove; - // For teleports and crossings callbacks - string m_callbackURI; - UUID m_originRegionID; + #region For teleports and crossings callbacks + + /// + /// In the V1 teleport protocol, the destination simulator sends ReleaseAgent to this address. + /// + private string m_callbackURI; + + /// + /// Records the region from which this presence originated, if not from login. + /// + /// + /// Also acts as a signal in the teleport V2 process to release UpdateAgent after a viewer has triggered + /// CompleteMovement and made the previous child agent a root agent. + /// + private UUID m_originRegionID; + + /// + /// This object is used as a lock before accessing m_originRegionID to make sure that every thread is seeing + /// the very latest value and not using some cached version. Cannot make m_originRegionID itself volatite as + /// it is a value type. + /// + private object m_originRegionIDAccessLock = new object(); + + /// + /// Triggered on entity transfer after to allow CompleteMovement() to proceed after we have received an + /// UpdateAgent from the originating region.ddkjjkj + /// + private AutoResetEvent m_updateAgentReceivedAfterTransferEvent = new AutoResetEvent(false); + + /// + /// Used by the entity transfer module to signal when the presence should not be closed because a subsequent + /// teleport is reusing the connection. + /// + /// May be refactored or move somewhere else soon. + public bool DoNotCloseAfterTeleport { get; set; } + + #endregion /// /// Script engines present in the scene @@ -295,15 +376,17 @@ namespace OpenSim.Region.Framework.Scenes /// /// Record user movement inputs. /// - public byte MovementFlag { get; private set; } + public uint MovementFlag { get; private set; } - private bool m_updateflag; + /// + /// Set this if we need to force a movement update on the next received AgentUpdate from the viewer. + /// + private const uint ForceUpdateMovementFlagValue = uint.MaxValue; - public bool Updated - { - set { m_updateflag = value; } - get { return m_updateflag; } - } + /// + /// Is the agent stop control flag currently active? + /// + public bool AgentControlStopActive { get; private set; } private bool m_invulnerable = true; @@ -344,6 +427,9 @@ namespace OpenSim.Region.Framework.Scenes /// protected Vector3 m_lastCameraPosition; + private Vector4 m_lastCameraCollisionPlane = new Vector4(0f, 0f, 0f, 1); + private bool m_doingCamRayCast = false; + public Vector3 CameraPosition { get; set; } public Quaternion CameraRotation @@ -375,7 +461,19 @@ namespace OpenSim.Region.Framework.Scenes public string Firstname { get; private set; } public string Lastname { get; private set; } - public string Grouptitle { get; set; } + public string Grouptitle + { + get { return UseFakeGroupTitle ? "(Loading)" : m_groupTitle; } + set { m_groupTitle = value; } + } + private string m_groupTitle; + + /// + /// When this is 'true', return a dummy group title instead of the real group title. This is + /// used as part of a hack to force viewers to update the displayed avatar name. + /// + public bool UseFakeGroupTitle { get; set; } + // Agent's Draw distance. public float DrawDistance { get; set; } @@ -424,7 +522,9 @@ namespace OpenSim.Region.Framework.Scenes get { return (IClientCore)ControllingClient; } } - public Vector3 ParentPosition { get; set; } + public UUID COF { get; set; } + +// public Vector3 ParentPosition { get; set; } /// /// Position of this avatar relative to the region the avatar is in @@ -437,12 +537,13 @@ namespace OpenSim.Region.Framework.Scenes { m_pos = PhysicsActor.Position; - //m_log.DebugFormat( - // "[SCENE PRESENCE]: Set position {0} for {1} in {2} via getting AbsolutePosition!", - // m_pos, Name, Scene.RegionInfo.RegionName); +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Set position of {0} in {1} to {2} via getting AbsolutePosition!", +// Name, Scene.Name, m_pos); } else { +// m_log.DebugFormat("[SCENE PRESENCE]: Fetching abs pos where PhysicsActor == null and parent part {0} for {1}", Name, Scene.Name); // Obtain the correct position of a seated avatar. // In addition to providing the correct position while // the avatar is seated, this value will also @@ -459,13 +560,16 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectPart sitPart = ParentPart; if (sitPart != null) - return sitPart.AbsolutePosition + (m_pos * sitPart.GetWorldRotation()); + return sitPart.ParentGroup.AbsolutePosition + (m_pos * sitPart.GetWorldRotation()); } return m_pos; } set { +// m_log.DebugFormat("[SCENE PRESENCE]: Setting position of {0} to {1} in {2}", Name, value, Scene.Name); +// Util.PrintCallStack(); + if (PhysicsActor != null) { try @@ -480,10 +584,7 @@ namespace OpenSim.Region.Framework.Scenes // Don't update while sitting. The PhysicsActor above is null whilst sitting. if (ParentID == 0) - { m_pos = value; - ParentPosition = Vector3.Zero; - } //m_log.DebugFormat( // "[ENTITY BASE]: In {0} set AbsolutePosition of {1} to {2}", @@ -513,8 +614,11 @@ namespace OpenSim.Region.Framework.Scenes } /// - /// Current velocity of the avatar. + /// Velocity of the avatar with respect to its local reference frame. /// + /// + /// So when sat on a vehicle this will be 0. To get velocity with respect to the world use GetWorldVelocity() + /// public override Vector3 Velocity { get @@ -527,11 +631,21 @@ namespace OpenSim.Region.Framework.Scenes // "[SCENE PRESENCE]: Set velocity {0} for {1} in {2} via getting Velocity!", // m_velocity, Name, Scene.RegionInfo.RegionName); } +// else if (ParentPart != null) +// { +// return ParentPart.ParentGroup.Velocity; +// } return m_velocity; } + set { +// Util.PrintCallStack(); +// m_log.DebugFormat( +// "[SCENE PRESENCE]: In {0} set velocity of {1} to {2}", +// Scene.RegionInfo.RegionName, Name, value); + if (PhysicsActor != null) { try @@ -544,37 +658,86 @@ namespace OpenSim.Region.Framework.Scenes } } - m_velocity = value; - -// m_log.DebugFormat( -// "[SCENE PRESENCE]: In {0} set velocity of {1} to {2}", -// Scene.RegionInfo.RegionName, Name, m_velocity); + m_velocity = value; } } +/* + public override Vector3 AngularVelocity + { + get + { + if (PhysicsActor != null) + { + m_rotationalvelocity = PhysicsActor.RotationalVelocity; + + // m_log.DebugFormat( + // "[SCENE PRESENCE]: Set velocity {0} for {1} in {2} via getting Velocity!", + // m_velocity, Name, Scene.RegionInfo.RegionName); + } + return m_rotationalvelocity; + } + } +*/ private Quaternion m_bodyRot = Quaternion.Identity; + /// + /// The rotation of the avatar. + /// + /// + /// If the avatar is not sitting, this is with respect to the world + /// If the avatar is sitting, this is a with respect to the part that it's sitting upon (a local rotation). + /// If you always want the world rotation, use GetWorldRotation() + /// public Quaternion Rotation { - get { return m_bodyRot; } + get + { + return m_bodyRot; + } + set { m_bodyRot = value; + if (PhysicsActor != null) { - PhysicsActor.Orientation = m_bodyRot; + try + { + PhysicsActor.Orientation = m_bodyRot; + } + catch (Exception e) + { + m_log.Error("[SCENE PRESENCE]: Orientation " + e.Message); + } } // m_log.DebugFormat("[SCENE PRESENCE]: Body rot for {0} set to {1}", Name, m_bodyRot); } } + // Used for limited viewer 'fake' user rotations. + private Vector3 m_AngularVelocity = Vector3.Zero; + + public Vector3 AngularVelocity + { + get { return m_AngularVelocity; } + } + public bool IsChildAgent { get; set; } + public bool IsLoggingIn { get; set; } /// /// If the avatar is sitting, the local ID of the prim that it's sitting on. If not sitting then zero. /// public uint ParentID { get; set; } + public UUID ParentUUID + { + get { return m_parentUUID; } + set { m_parentUUID = value; } + } + private UUID m_parentUUID = UUID.Zero; + /// /// Are we sitting on an object? /// @@ -595,6 +758,33 @@ namespace OpenSim.Region.Framework.Scenes set { m_health = value; } } + /// + /// Get rotation relative to the world. + /// + /// + public Quaternion GetWorldRotation() + { + SceneObjectPart sitPart = ParentPart; + + if (sitPart != null) + return sitPart.GetWorldRotation() * Rotation; + + return Rotation; + } + + /// + /// Get velocity relative to the world. + /// + public Vector3 GetWorldVelocity() + { + SceneObjectPart sitPart = ParentPart; + + if (sitPart != null) + return sitPart.ParentGroup.Velocity; + + return Velocity; + } + public void AdjustKnownSeeds() { Dictionary seeds; @@ -608,9 +798,8 @@ namespace OpenSim.Region.Framework.Scenes foreach (ulong handle in seeds.Keys) { uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(handle, out x, out y); + if (Util.IsOutsideView(DrawDistance, x, Scene.RegionInfo.RegionLocX, y, Scene.RegionInfo.RegionLocY)) { old.Add(handle); @@ -632,18 +821,22 @@ namespace OpenSim.Region.Framework.Scenes foreach (KeyValuePair kvp in KnownRegions) { uint x, y; - Utils.LongToUInts(kvp.Key, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(kvp.Key, out x, out y); m_log.Info(" >> "+x+", "+y+": "+kvp.Value); } } private bool m_mouseLook; - private bool m_leftButtonDown; +// private bool m_leftButtonDown; private bool m_inTransit; + /// + /// This signals whether the presence is in transit between neighbouring regions. + /// + /// + /// It is not set when the presence is teleporting or logging in/out directly to a region. + /// public bool IsInTransit { get { return m_inTransit; } @@ -667,6 +860,14 @@ namespace OpenSim.Region.Framework.Scenes set { m_speedModifier = value; } } + /// + /// Modifier for agent movement if we get an AGENT_CONTROL_STOP whilst walking or running + /// + /// + /// AGENT_CONTRL_STOP comes about if user holds down space key on viewers. + /// + private float AgentControlStopSlowWhilstMoving = 0.5f; + private bool m_forceFly; public bool ForceFly @@ -685,23 +886,30 @@ namespace OpenSim.Region.Framework.Scenes public string Viewer { - get { return m_scene.AuthenticateHandler.GetAgentCircuitData(ControllingClient.CircuitCode).Viewer; } + get { return Util.GetViewerName(m_scene.AuthenticateHandler.GetAgentCircuitData(ControllingClient.CircuitCode)); } } + /// + /// Count of how many terse updates we have sent out. It doesn't matter if this overflows. + /// + private int m_terseUpdateCount; + #endregion #region Constructor(s) public ScenePresence( IClientAPI client, Scene world, AvatarAppearance appearance, PresenceType type) - { + { AttachmentsSyncLock = new Object(); AllowMovement = true; IsChildAgent = true; + IsLoggingIn = false; m_sendCoarseLocationsMethod = SendCoarseLocationsDefault; Animator = new ScenePresenceAnimator(this); PresenceType = type; - DrawDistance = world.DefaultDrawDistance; + // DrawDistance = world.DefaultDrawDistance; + DrawDistance = Constants.RegionSize; RegionHandle = world.RegionInfo.RegionHandle; ControllingClient = client; Firstname = ControllingClient.FirstName; @@ -739,12 +947,42 @@ namespace OpenSim.Region.Framework.Scenes SetDirectionVectors(); Appearance = appearance; + + m_stateMachine = new ScenePresenceStateMachine(this); + } + + private void RegionHeartbeatEnd(Scene scene) + { + if (IsChildAgent) + return; + + m_movementAnimationUpdateCounter ++; + if (m_movementAnimationUpdateCounter >= 2) + { + m_movementAnimationUpdateCounter = 0; + if (Animator != null) + { + // If the parentID == 0 we are not sitting + // if !SitGournd then we are not sitting on the ground + // Fairly straightforward, now here comes the twist + // if ParentUUID is NOT UUID.Zero, we are looking to + // be sat on an object that isn't there yet. Should + // be treated as if sat. + if(ParentID == 0 && !SitGround && ParentUUID == UUID.Zero) // skip it if sitting + Animator.UpdateMovementAnimations(); + } + else + { + m_scene.EventManager.OnRegionHeartbeatEnd -= RegionHeartbeatEnd; + } + } } public void RegisterToEvents() { ControllingClient.OnCompleteMovementToRegion += CompleteMovement; ControllingClient.OnAgentUpdate += HandleAgentUpdate; + ControllingClient.OnAgentCameraUpdate += HandleAgentCamerasUpdate; ControllingClient.OnAgentRequestSit += HandleAgentRequestSit; ControllingClient.OnAgentSit += HandleAgentSit; ControllingClient.OnSetAlwaysRun += HandleSetAlwaysRun; @@ -772,23 +1010,6 @@ namespace OpenSim.Region.Framework.Scenes Dir_Vectors[10] = new Vector3(0f, 0f, -0.5f); //DOWN_Nudge } - private Vector3[] GetWalkDirectionVectors() - { - Vector3[] vector = new Vector3[11]; - vector[0] = new Vector3(CameraUpAxis.Z, 0f, -CameraAtAxis.Z); //FORWARD - vector[1] = new Vector3(-CameraUpAxis.Z, 0f, CameraAtAxis.Z); //BACK - vector[2] = Vector3.UnitY; //LEFT - vector[3] = -Vector3.UnitY; //RIGHT - vector[4] = new Vector3(CameraAtAxis.Z, 0f, CameraUpAxis.Z); //UP - vector[5] = new Vector3(-CameraAtAxis.Z, 0f, -CameraUpAxis.Z); //DOWN - vector[6] = new Vector3(CameraUpAxis.Z, 0f, -CameraAtAxis.Z); //FORWARD_NUDGE - vector[7] = new Vector3(-CameraUpAxis.Z, 0f, CameraAtAxis.Z); //BACK_NUDGE - vector[8] = Vector3.UnitY; //LEFT_NUDGE - vector[9] = -Vector3.UnitY; //RIGHT_NUDGE - vector[10] = new Vector3(-CameraAtAxis.Z, 0f, -CameraUpAxis.Z); //DOWN_NUDGE - return vector; - } - #endregion #region Status Methods @@ -796,6 +1017,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// Turns a child agent into a root agent. /// + /// /// Child agents are logged into neighbouring sims largely to observe changes. Root agents exist when the /// avatar is actual in the sim. They can perform all actions. /// This change is made whenever an avatar enters a region, whether by crossing over from a neighbouring sim, @@ -803,80 +1025,188 @@ namespace OpenSim.Region.Framework.Scenes /// /// This method is on the critical path for transferring an avatar from one region to another. Delay here /// delays that crossing. - /// - public void MakeRootAgent(Vector3 pos, bool isFlying) + /// + private bool MakeRootAgent(Vector3 pos, bool isFlying) { - m_log.DebugFormat( - "[SCENE]: Upgrading child to root agent for {0} in {1}", - Name, m_scene.RegionInfo.RegionName); + lock (m_completeMovementLock) + { + if (!IsChildAgent) + return false; + + //m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); + + // m_log.InfoFormat( + // "[SCENE]: Upgrading child to root agent for {0} in {1}", + // Name, m_scene.RegionInfo.RegionName); + + if (ParentUUID != UUID.Zero) + { + m_log.DebugFormat("[SCENE PRESENCE]: Sitting avatar back on prim {0}", ParentUUID); + SceneObjectPart part = m_scene.GetSceneObjectPart(ParentUUID); + if (part == null) + { + m_log.ErrorFormat("[SCENE PRESENCE]: Can't find prim {0} to sit on", ParentUUID); + } + else + { + part.AddSittingAvatar(this); + // ParentPosition = part.GetWorldPosition(); + ParentID = part.LocalId; + ParentPart = part; + m_pos = PrevSitOffset; + // pos = ParentPosition; + pos = part.GetWorldPosition(); + } + ParentUUID = UUID.Zero; + + // Animator.TrySetMovementAnimation("SIT"); + } + else + { + IsLoggingIn = false; + } - //m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); + IsChildAgent = false; + } - bool wasChild = IsChildAgent; - IsChildAgent = false; + // Must reset this here so that a teleport to a region next to an existing region does not keep the flag + // set and prevent the close of the connection on a subsequent re-teleport. + // Should not be needed if we are not trying to tell this region to close +// DoNotCloseAfterTeleport = false; IGroupsModule gm = m_scene.RequestModuleInterface(); if (gm != null) Grouptitle = gm.GetGroupTitle(m_uuid); + AgentCircuitData aCircuit = m_scene.AuthenticateHandler.GetAgentCircuitData(ControllingClient.CircuitCode); + uint teleportFlags = (aCircuit == null) ? 0 : aCircuit.teleportFlags; + if ((teleportFlags & (uint)TeleportFlags.ViaHGLogin) != 0) + { + // The avatar is arriving from another grid. This means that we may have changed the + // avatar's name to or from the special Hypergrid format ("First.Last @grid.example.com"). + // Unfortunately, due to a viewer bug, viewers don't always show the new name. + // But we have a trick that can force them to update the name anyway. + ForceViewersUpdateName(); + } + RegionHandle = m_scene.RegionInfo.RegionHandle; m_scene.EventManager.TriggerSetRootAgentScene(m_uuid, m_scene); - // Moved this from SendInitialData to ensure that Appearance is initialized - // before the inventory is processed in MakeRootAgent. This fixes a race condition - // related to the handling of attachments - //m_scene.GetAvatarAppearance(ControllingClient, out Appearance); - if (m_scene.TestBorderCross(pos, Cardinals.E)) + UUID groupUUID = ControllingClient.ActiveGroupId; + string groupName = string.Empty; + ulong groupPowers = 0; + + // ---------------------------------- + // Previous Agent Difference - AGNI sends an unsolicited AgentDataUpdate upon root agent status + try { - Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.E); - pos.X = crossedBorder.BorderLine.Z - 1; - } + if (groupUUID != UUID.Zero && gm != null) + { + GroupRecord record = gm.GetGroupRecord(groupUUID); + if (record != null) + groupName = record.GroupName; + + GroupMembershipData groupMembershipData = gm.GetMembershipData(groupUUID, m_uuid); - if (m_scene.TestBorderCross(pos, Cardinals.N)) + if (groupMembershipData != null) + groupPowers = groupMembershipData.GroupPowers; + } + + ControllingClient.SendAgentDataUpdate( + m_uuid, groupUUID, Firstname, Lastname, groupPowers, groupName, Grouptitle); + } + catch (Exception e) { - Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.N); - pos.Y = crossedBorder.BorderLine.Z - 1; + m_log.Error("[AGENTUPDATE]: Error ", e); } + // ------------------------------------ - CheckAndAdjustLandingPoint(ref pos); - - if (pos.X < 0f || pos.Y < 0f || pos.Z < 0f) + if (ParentID == 0) { - m_log.WarnFormat( - "[SCENE PRESENCE]: MakeRootAgent() was given an illegal position of {0} for avatar {1}, {2}. Clamping", - pos, Name, UUID); + // Moved this from SendInitialData to ensure that Appearance is initialized + // before the inventory is processed in MakeRootAgent. This fixes a race condition + // related to the handling of attachments + //m_scene.GetAvatarAppearance(ControllingClient, out Appearance); + + /* RA 20140111: Commented out these TestBorderCross's. + * Not sure why this code is here. It is not checking all the borders + * and 'in region' sanity checking is done in CheckAndAdjustLandingPoint and below. + if (m_scene.TestBorderCross(pos, Cardinals.E)) + { + Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.E); + pos.X = crossedBorder.BorderLine.Z - 1; + } - if (pos.X < 0f) pos.X = 0f; - if (pos.Y < 0f) pos.Y = 0f; - if (pos.Z < 0f) pos.Z = 0f; - } + if (m_scene.TestBorderCross(pos, Cardinals.N)) + { + Border crossedBorder = m_scene.GetCrossedBorder(pos, Cardinals.N); + pos.Y = crossedBorder.BorderLine.Z - 1; + } + */ + + CheckAndAdjustLandingPoint(ref pos); + + if (pos.X < 0f || pos.Y < 0f || pos.Z < 0f) + { + m_log.WarnFormat( + "[SCENE PRESENCE]: MakeRootAgent() was given an illegal position of {0} for avatar {1}, {2}. Clamping", + pos, Name, UUID); - float localAVHeight = 1.56f; - if (Appearance.AvatarHeight > 0) - localAVHeight = Appearance.AvatarHeight; + if (pos.X < 0f) pos.X = 0f; + if (pos.Y < 0f) pos.Y = 0f; + if (pos.Z < 0f) pos.Z = 0f; + } - float posZLimit = 0; + float localAVHeight = 1.56f; + if (Appearance.AvatarHeight > 0) + localAVHeight = Appearance.AvatarHeight; - if (pos.X < Constants.RegionSize && pos.Y < Constants.RegionSize) - posZLimit = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y]; - - float newPosZ = posZLimit + localAVHeight / 2; - if (posZLimit >= (pos.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) - { - pos.Z = newPosZ; - } - AbsolutePosition = pos; + float posZLimit = 0; - AddToPhysicalScene(isFlying); + if (pos.X < m_scene.RegionInfo.RegionSizeX && pos.Y < m_scene.RegionInfo.RegionSizeY) + posZLimit = (float)m_scene.Heightmap[(int)pos.X, (int)pos.Y]; + + float newPosZ = posZLimit + localAVHeight / 2; + if (posZLimit >= (pos.Z - (localAVHeight / 2)) && !(Single.IsInfinity(newPosZ) || Single.IsNaN(newPosZ))) + { + pos.Z = newPosZ; + } + AbsolutePosition = pos; - if (ForceFly) - { - Flying = true; - } - else if (FlyDisabled) - { - Flying = false; +// m_log.DebugFormat( +// "Set pos {0}, vel {1} in {1} to {2} from input position of {3} on MakeRootAgent", +// Name, Scene.Name, AbsolutePosition, pos); +// + if (m_teleportFlags == TeleportFlags.Default) + { + AddToPhysicalScene(isFlying); +// +// Console.WriteLine( +// "Set velocity of {0} in {1} to {2} from input velocity of {3} on MakeRootAgent", +// Name, Scene.Name, PhysicsActor.Velocity, vel); +// } + } + else + { + AddToPhysicalScene(isFlying); + } + + // XXX: This is to trigger any secondary teleport needed for a megaregion when the user has teleported to a + // location outside the 'root region' (the south-west 256x256 corner). This is the earlist we can do it + // since it requires a physics actor to be present. If it is left any later, then physics appears to reset + // the value to a negative position which does not trigger the border cross. + // This may not be the best location for this. + CheckForBorderCrossing(); + + if (ForceFly) + { + Flying = true; + } + else if (FlyDisabled) + { + Flying = false; + } } // Don't send an animation pack here, since on a region crossing this will sometimes cause a flying @@ -886,26 +1216,33 @@ namespace OpenSim.Region.Framework.Scenes m_scene.SwapRootAgentCount(false); - // The initial login scene presence is already root when it gets here - // and it has already rezzed the attachments and started their scripts. - // We do the following only for non-login agents, because their scripts - // haven't started yet. - lock (m_attachments) + if (Scene.AttachmentsModule != null) { - if (wasChild && HasAttachments()) + // The initial login scene presence is already root when it gets here + // and it has already rezzed the attachments and started their scripts. + // We do the following only for non-login agents, because their scripts + // haven't started yet. + if (PresenceType == PresenceType.Npc || IsRealLogin(m_teleportFlags)) { - m_log.DebugFormat( - "[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name); - - // Resume scripts - foreach (SceneObjectGroup sog in m_attachments) - { - sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource()); - sog.ResumeScripts(); - } + WorkManager.RunJob( + "RezAttachments", + o => Scene.AttachmentsModule.RezAttachments(this), + null, + string.Format("Rez attachments for {0} in {1}", Name, Scene.Name)); + } + else + { + WorkManager.RunJob( + "StartAttachmentScripts", + o => RestartAttachmentScripts(), + null, + string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name), + true); } } + SendAvatarDataToAllClients(); + // send the animations of the other presences to me m_scene.ForEachRootScenePresence(delegate(ScenePresence presence) { @@ -916,27 +1253,93 @@ namespace OpenSim.Region.Framework.Scenes // If we don't reset the movement flag here, an avatar that crosses to a neighbouring sim and returns will // stall on the border crossing since the existing child agent will still have the last movement // recorded, which stops the input from being processed. - MovementFlag = 0; + MovementFlag = ForceUpdateMovementFlagValue; m_scene.EventManager.TriggerOnMakeRootAgent(this); + + return true; } - public int GetStateSource() + private void RestartAttachmentScripts() { - AgentCircuitData aCircuit = m_scene.AuthenticateHandler.GetAgentCircuitData(UUID); + // We need to restart scripts here so that they receive the correct changed events (CHANGED_TELEPORT + // and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently + // be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are + // not transporting the required data. + // + // We must take a copy of the attachments list here (rather than locking) to avoid a deadlock where a script in one of + // the attachments may start processing an event (which locks ScriptInstance.m_Script) that then calls a method here + // which needs to lock m_attachments. ResumeScripts() needs to take a ScriptInstance.m_Script lock to try to unset the Suspend status. + // + // FIXME: In theory, this deadlock should not arise since scripts should not be processing events until ResumeScripts(). + // But XEngine starts all scripts unsuspended. Starting them suspended will not currently work because script rezzing + // is placed in an asynchronous queue in XEngine and so the ResumeScripts() call will almost certainly execute before the + // script is rezzed. This means the ResumeScripts() does absolutely nothing when using XEngine. + List attachments = GetAttachments(); - if (aCircuit != null && (aCircuit.teleportFlags != (uint)TeleportFlags.Default)) - { - // This will get your attention - //m_log.Error("[XXX] Triggering CHANGED_TELEPORT"); + m_log.DebugFormat( + "[SCENE PRESENCE]: Restarting scripts in {0} attachments for {1} in {2}", attachments.Count, Name, Scene.Name); - return 5; // StateSource.Teleporting + // Resume scripts + foreach (SceneObjectGroup sog in attachments) + { + sog.ScheduleGroupForFullUpdate(); + sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource()); + sog.ResumeScripts(); } - return 2; // StateSource.PrimCrossing + } + + private static bool IsRealLogin(TeleportFlags teleportFlags) + { + return ((teleportFlags & TeleportFlags.ViaLogin) != 0) && ((teleportFlags & TeleportFlags.ViaHGLogin) == 0); } /// - /// This turns a root agent into a child agent + /// Force viewers to show the avatar's current name. + /// + /// + /// The avatar name that is shown above the avatar in the viewers is sent in ObjectUpdate packets, + /// and they get the name from the ScenePresence. Unfortunately, viewers have a bug (as of April 2014) + /// where they ignore changes to the avatar name. However, tey don't ignore changes to the avatar's + /// Group Title. So the following trick makes viewers update the avatar's name by briefly changing + /// the group title (to "(Loading)"), and then restoring it. + /// + public void ForceViewersUpdateName() + { + m_log.DebugFormat("[SCENE PRESENCE]: Forcing viewers to update the avatar name for " + Name); + + UseFakeGroupTitle = true; + SendAvatarDataToAllClients(false); + + Util.FireAndForget(o => + { + // Viewers only update the avatar name when idle. Therefore, we must wait long + // enough for the viewer to show the fake name that we had set above, and only + // then switch back to the true name. This delay was chosen because it has a high + // chance of succeeding (we don't want to choose a value that's too low). + Thread.Sleep(5000); + + UseFakeGroupTitle = false; + SendAvatarDataToAllClients(false); + }, null, "Scenepresence.ForceViewersUpdateName"); + } + + public int GetStateSource() + { + AgentCircuitData aCircuit = m_scene.AuthenticateHandler.GetAgentCircuitData(UUID); + + if (aCircuit != null && (aCircuit.teleportFlags != (uint)TeleportFlags.Default)) + { + // This will get your attention + //m_log.Error("[XXX] Triggering CHANGED_TELEPORT"); + + return 5; // StateSource.Teleporting + } + return 2; // StateSource.PrimCrossing + } + + /// + /// This turns a root agent into a child agent /// /// /// when an agent departs this region for a neighbor, this gets called. @@ -946,12 +1349,21 @@ namespace OpenSim.Region.Framework.Scenes /// public void MakeChildAgent() { + m_scene.EventManager.OnRegionHeartbeatEnd -= RegionHeartbeatEnd; + m_log.DebugFormat("[SCENE PRESENCE]: Making {0} a child agent in {1}", Name, Scene.RegionInfo.RegionName); + // Reset the m_originRegionID as it has dual use as a flag to signal that the UpdateAgent() call orignating + // from the source simulator has completed on a V2 teleport. + lock (m_originRegionIDAccessLock) + m_originRegionID = UUID.Zero; + // Reset these so that teleporting in and walking out isn't seen // as teleporting back TeleportFlags = TeleportFlags.Default; + MovementFlag = 0; + // It looks like Animator is set to null somewhere, and MakeChild // is called after that. Probably in aborted teleports. if (Animator == null) @@ -959,6 +1371,7 @@ namespace OpenSim.Region.Framework.Scenes else Animator.ResetAnimations(); + // m_log.DebugFormat( // "[SCENE PRESENCE]: Downgrading root agent {0}, {1} to a child agent in {2}", // Name, UUID, m_scene.RegionInfo.RegionName); @@ -970,6 +1383,7 @@ namespace OpenSim.Region.Framework.Scenes IsChildAgent = true; m_scene.SwapRootAgentCount(true); RemoveFromPhysicalScene(); + ParentID = 0; // Child agents can't be sitting // FIXME: Set RegionHandle to the region handle of the scene this agent is moving into @@ -984,10 +1398,10 @@ namespace OpenSim.Region.Framework.Scenes if (PhysicsActor != null) { // PhysicsActor.OnRequestTerseUpdate -= SendTerseUpdateToAllClients; - PhysicsActor.OnOutOfBounds -= OutOfBoundsCall; - m_scene.PhysicsScene.RemoveAvatar(PhysicsActor); PhysicsActor.UnSubscribeEvents(); + PhysicsActor.OnOutOfBounds -= OutOfBoundsCall; PhysicsActor.OnCollisionUpdate -= PhysicsCollisionUpdate; + m_scene.PhysicsScene.RemoveAvatar(PhysicsActor); PhysicsActor = null; } // else @@ -1004,7 +1418,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void Teleport(Vector3 pos) { - TeleportWithMomentum(pos, null); + TeleportWithMomentum(pos, Vector3.Zero); } public void TeleportWithMomentum(Vector3 pos, Vector3? v) @@ -1024,14 +1438,141 @@ namespace OpenSim.Region.Framework.Scenes else PhysicsActor.SetMomentum(vel); } + } + + public void avnLocalTeleport(Vector3 newpos, Vector3? newvel, bool rotateToVelXY) + { + CheckLandingPoint(ref newpos); + AbsolutePosition = newpos; - SendTerseUpdateToAllClients(); + if (newvel.HasValue) + { + if ((Vector3)newvel == Vector3.Zero) + { + if (PhysicsActor != null) + PhysicsActor.SetMomentum(Vector3.Zero); + m_velocity = Vector3.Zero; + } + else + { + if (PhysicsActor != null) + PhysicsActor.SetMomentum((Vector3)newvel); + m_velocity = (Vector3)newvel; + + if (rotateToVelXY) + { + Vector3 lookAt = (Vector3)newvel; + lookAt.Z = 0; + lookAt.Normalize(); + ControllingClient.SendLocalTeleport(newpos, lookAt, (uint)TeleportFlags.ViaLocation); + return; + } + } + } } public void StopFlying() { - ControllingClient.StopFlying(this); + Vector3 pos = AbsolutePosition; + if (Appearance.AvatarHeight != 127.0f) + pos += new Vector3(0f, 0f, (Appearance.AvatarHeight / 6f)); + else + pos += new Vector3(0f, 0f, (1.56f / 6f)); + + AbsolutePosition = pos; + + // attach a suitable collision plane regardless of the actual situation to force the LLClient to land. + // Collision plane below the avatar's position a 6th of the avatar's height is suitable. + // Mind you, that this method doesn't get called if the avatar's velocity magnitude is greater then a + // certain amount.. because the LLClient wouldn't land in that situation anyway. + + // why are we still testing for this really old height value default??? + if (Appearance.AvatarHeight != 127.0f) + CollisionPlane = new Vector4(0, 0, 0, pos.Z - Appearance.AvatarHeight / 6f); + else + CollisionPlane = new Vector4(0, 0, 0, pos.Z - (1.56f / 6f)); + + ControllingClient.SendAgentTerseUpdate(this); + } + + /// + /// Applies a roll accumulator to the avatar's angular velocity for the avatar fly roll effect. + /// + /// Postive or negative roll amount in radians + private void ApplyFlyingRoll(float amount, bool PressingUp, bool PressingDown) + { + + float rollAmount = Util.Clamp(m_AngularVelocity.Z + amount, -FLY_ROLL_MAX_RADIANS, FLY_ROLL_MAX_RADIANS); + m_AngularVelocity.Z = rollAmount; + + // APPLY EXTRA consideration for flying up and flying down during this time. + // if we're turning left + if (amount > 0) + { + + // If we're at the max roll and pressing up, we want to swing BACK a bit + // Automatically adds noise + if (PressingUp) + { + if (m_AngularVelocity.Z >= FLY_ROLL_MAX_RADIANS - 0.04f) + m_AngularVelocity.Z -= 0.9f; + } + // If we're at the max roll and pressing down, we want to swing MORE a bit + if (PressingDown) + { + if (m_AngularVelocity.Z >= FLY_ROLL_MAX_RADIANS && m_AngularVelocity.Z < FLY_ROLL_MAX_RADIANS + 0.6f) + m_AngularVelocity.Z += 0.6f; + } + } + else // we're turning right. + { + // If we're at the max roll and pressing up, we want to swing BACK a bit + // Automatically adds noise + if (PressingUp) + { + if (m_AngularVelocity.Z <= (-FLY_ROLL_MAX_RADIANS)) + m_AngularVelocity.Z += 0.6f; + } + // If we're at the max roll and pressing down, we want to swing MORE a bit + if (PressingDown) + { + if (m_AngularVelocity.Z >= -FLY_ROLL_MAX_RADIANS - 0.6f) + m_AngularVelocity.Z -= 0.6f; + } + } + } + + /// + /// incrementally sets roll amount to zero + /// + /// Positive roll amount in radians + /// + private float CalculateFlyingRollResetToZero(float amount) + { + const float rollMinRadians = 0f; + + if (m_AngularVelocity.Z > 0) + { + + float leftOverToMin = m_AngularVelocity.Z - rollMinRadians; + if (amount > leftOverToMin) + return -leftOverToMin; + else + return -amount; + + } + else + { + + float leftOverToMin = -m_AngularVelocity.Z - rollMinRadians; + if (amount > leftOverToMin) + return leftOverToMin; + else + return amount; + } } + + // neighbouring regions we have enabled a child agent in // holds the seed cap for the child agent in that region @@ -1118,6 +1659,40 @@ namespace OpenSim.Region.Framework.Scenes PhysicsActor.Size = new Vector3(0.45f, 0.6f, height); } + public void SetSize(Vector3 size, float feetoffset) + { + if (PhysicsActor != null && !IsChildAgent) + { + // Eventually there will be a physics call that sets avatar size that includes offset info. + // For the moment, just set the size as passed. + PhysicsActor.Size = size; + // PhysicsActor.setAvatarSize(size, feetoffset); + } + } + + private bool WaitForUpdateAgent(IClientAPI client) + { + // Before the source region executes UpdateAgent + // (which triggers Scene.IncomingUpdateChildAgent(AgentData cAgentData) here in the destination, + // m_originRegionID is UUID.Zero; after, it's non-Zero. The CompleteMovement sequence initiated from the + // viewer (in turn triggered by the source region sending it a TeleportFinish event) waits until it's non-zero + m_updateAgentReceivedAfterTransferEvent.WaitOne(10000); + + UUID originID = UUID.Zero; + + lock (m_originRegionIDAccessLock) + originID = m_originRegionID; + + if (originID.Equals(UUID.Zero)) + { + // Movement into region will fail + m_log.WarnFormat("[SCENE PRESENCE]: Update agent {0} never arrived in {1}", client.Name, Scene.Name); + return false; + } + + return true; + } + /// /// Complete Avatar's movement into the region. /// @@ -1131,74 +1706,131 @@ namespace OpenSim.Region.Framework.Scenes { // DateTime startTime = DateTime.Now; - m_log.DebugFormat( + m_log.InfoFormat( "[SCENE PRESENCE]: Completing movement of {0} into region {1} in position {2}", - client.Name, Scene.RegionInfo.RegionName, AbsolutePosition); + client.Name, Scene.Name, AbsolutePosition); - Vector3 look = Velocity; + bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); // Get this ahead of time because IsInTransit modifies 'm_AgentControlFlags' - if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) + IsInTransit = true; + try { - look = new Vector3(0.99f, 0.042f, 0); - } + // Make sure it's not a login agent. We don't want to wait for updates during login + if (!(PresenceType == PresenceType.Npc || IsRealLogin(m_teleportFlags))) + { + // Let's wait until UpdateAgent (called by departing region) is done + if (!WaitForUpdateAgent(client)) + // The sending region never sent the UpdateAgent data, we have to refuse + return; + } - // Prevent teleporting to an underground location - // (may crash client otherwise) - // - Vector3 pos = AbsolutePosition; - float ground = m_scene.GetGroundHeight(pos.X, pos.Y); - if (pos.Z < ground + 1.5f) - { - pos.Z = ground + 1.5f; - AbsolutePosition = pos; - } + Vector3 look = Velocity; - bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); - MakeRootAgent(AbsolutePosition, flying); - ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); + // if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) + if ((Math.Abs(look.X) < 0.1) && (Math.Abs(look.Y) < 0.1) && (Math.Abs(look.Z) < 0.1)) + { + look = new Vector3(0.99f, 0.042f, 0); + } -// m_log.DebugFormat("[SCENE PRESENCE] Completed movement"); + // Prevent teleporting to an underground location + // (may crash client otherwise) + // + Vector3 pos = AbsolutePosition; + float ground = m_scene.GetGroundHeight(pos.X, pos.Y); + if (pos.Z < ground + 1.5f) + { + pos.Z = ground + 1.5f; + AbsolutePosition = pos; + } - if ((m_callbackURI != null) && !m_callbackURI.Equals("")) - { - // We cannot sleep here since this would hold up the inbound packet processing thread, as - // CompleteMovement() is executed synchronously. However, it might be better to delay the release - // here until we know for sure that the agent is active in this region. Sending AgentMovementComplete - // is not enough for Imprudence clients - there appears to be a small delay (<200ms, <500ms) until they regard this - // region as the current region, meaning that a close sent before then will fail the teleport. -// System.Threading.Thread.Sleep(2000); + if (!MakeRootAgent(AbsolutePosition, flying)) + { + m_log.DebugFormat( + "[SCENE PRESENCE]: Aborting CompleteMovement call for {0} in {1} as they are already root", + Name, Scene.Name); - m_log.DebugFormat( - "[SCENE PRESENCE]: Releasing {0} {1} with callback to {2}", - client.Name, client.AgentId, m_callbackURI); + return; + } - Scene.SimulationService.ReleaseAgent(m_originRegionID, UUID, m_callbackURI); - m_callbackURI = null; - } -// else -// { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: No callback provided on CompleteMovement of {0} {1} to {2}", -// client.Name, client.AgentId, m_scene.RegionInfo.RegionName); -// } + // Tell the client that we're totally ready + ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); - ValidateAndSendAppearanceAndAgentData(); + // Child agents send initial data up in LLUDPServer.HandleUseCircuitCode() + if (!SentInitialDataToClient) + SendInitialDataToClient(); - // Create child agents in neighbouring regions - if (openChildAgents && !IsChildAgent) - { - IEntityTransferModule m_agentTransfer = m_scene.RequestModuleInterface(); - if (m_agentTransfer != null) - Util.FireAndForget(delegate { m_agentTransfer.EnableChildAgents(this); }); + // m_log.DebugFormat("[SCENE PRESENCE] Completed movement"); + + if (!string.IsNullOrEmpty(m_callbackURI)) + { + // We cannot sleep here since this would hold up the inbound packet processing thread, as + // CompleteMovement() is executed synchronously. However, it might be better to delay the release + // here until we know for sure that the agent is active in this region. Sending AgentMovementComplete + // is not enough for Imprudence clients - there appears to be a small delay (<200ms, <500ms) until they regard this + // region as the current region, meaning that a close sent before then will fail the teleport. + // System.Threading.Thread.Sleep(2000); + + m_log.DebugFormat( + "[SCENE PRESENCE]: Releasing {0} {1} with callback to {2}", + client.Name, client.AgentId, m_callbackURI); + + Scene.SimulationService.ReleaseAgent(m_originRegionID, UUID, m_callbackURI); + m_callbackURI = null; + } + // else + // { + // m_log.DebugFormat( + // "[SCENE PRESENCE]: No callback provided on CompleteMovement of {0} {1} to {2}", + // client.Name, client.AgentId, m_scene.RegionInfo.RegionName); + // } + + ValidateAndSendAppearanceAndAgentData(); + + // Create child agents in neighbouring regions + if (openChildAgents && !IsChildAgent) + { + IEntityTransferModule m_agentTransfer = m_scene.RequestModuleInterface(); + if (m_agentTransfer != null) + { + // Note: this call can take a while, because it notifies each of the simulator's neighbours. + // It's important that we don't allow the avatar to cross regions meanwhile, as that will + // cause serious errors. We've prevented that from happening by setting IsInTransit=true. + m_agentTransfer.EnableChildAgents(this); + } + + IFriendsModule friendsModule = m_scene.RequestModuleInterface(); + if (friendsModule != null) + friendsModule.SendFriendsOnlineIfNeeded(ControllingClient); + + } - IFriendsModule friendsModule = m_scene.RequestModuleInterface(); - if (friendsModule != null) - friendsModule.SendFriendsOnlineIfNeeded(ControllingClient); + // XXX: If we force an update after activity has completed, then multiple attachments do appear correctly on a destination region + // If we do it a little bit earlier (e.g. when converting the child to a root agent) then this does not work. + // This may be due to viewer code or it may be something we're not doing properly simulator side. + WorkManager.RunJob( + "ScheduleAttachmentsForFullUpdate", + o => ScheduleAttachmentsForFullUpdate(), + null, + string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name), + true); + + // m_log.DebugFormat( + // "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms", + // client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds); } + finally + { + IsInTransit = false; + } + } -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms", -// client.Name, Scene.RegionInfo.RegionName, (DateTime.Now - startTime).Milliseconds); + private void ScheduleAttachmentsForFullUpdate() + { + lock (m_attachments) + { + foreach (SceneObjectGroup sog in m_attachments) + sog.ScheduleGroupForFullUpdate(); + } } /// @@ -1209,36 +1841,69 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// + /// + + private void UpdateCameraCollisionPlane(Vector4 plane) + { + if (m_lastCameraCollisionPlane != plane) + { + m_lastCameraCollisionPlane = plane; + ControllingClient.SendCameraConstraint(plane); + } + } + public void RayCastCameraCallback(bool hitYN, Vector3 collisionPoint, uint localid, float distance, Vector3 pNormal) { const float POSITION_TOLERANCE = 0.02f; - const float VELOCITY_TOLERANCE = 0.02f; const float ROTATION_TOLERANCE = 0.02f; - if (m_followCamAuto) + m_doingCamRayCast = false; + if (hitYN && localid != LocalId) { - if (hitYN) + SceneObjectGroup group = m_scene.GetGroupByPrim(localid); + bool IsPrim = group != null; + if (IsPrim) { - CameraConstraintActive = true; - //m_log.DebugFormat("[RAYCASTRESULT]: {0}, {1}, {2}, {3}", hitYN, collisionPoint, localid, distance); - - Vector3 normal = Vector3.Normalize(new Vector3(0f, 0f, collisionPoint.Z) - collisionPoint); - ControllingClient.SendCameraConstraint(new Vector4(normal.X, normal.Y, normal.Z, -1 * Vector3.Distance(new Vector3(0,0,collisionPoint.Z),collisionPoint))); + SceneObjectPart part = group.GetPart(localid); + if (part != null && !part.VolumeDetectActive) + { + CameraConstraintActive = true; + pNormal.X = (float) Math.Round(pNormal.X, 2); + pNormal.Y = (float) Math.Round(pNormal.Y, 2); + pNormal.Z = (float) Math.Round(pNormal.Z, 2); + pNormal.Normalize(); + collisionPoint.X = (float) Math.Round(collisionPoint.X, 1); + collisionPoint.Y = (float) Math.Round(collisionPoint.Y, 1); + collisionPoint.Z = (float) Math.Round(collisionPoint.Z, 1); + + Vector4 plane = new Vector4(pNormal.X, pNormal.Y, pNormal.Z, + Vector3.Dot(collisionPoint, pNormal)); + UpdateCameraCollisionPlane(plane); + } } else { - if (!m_pos.ApproxEquals(m_lastPosition, POSITION_TOLERANCE) || - !Velocity.ApproxEquals(m_lastVelocity, VELOCITY_TOLERANCE) || - !Rotation.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE)) - { - if (CameraConstraintActive) - { - ControllingClient.SendCameraConstraint(new Vector4(0f, 0.5f, 0.9f, -3000f)); - CameraConstraintActive = false; - } - } + CameraConstraintActive = true; + pNormal.X = (float) Math.Round(pNormal.X, 2); + pNormal.Y = (float) Math.Round(pNormal.Y, 2); + pNormal.Z = (float) Math.Round(pNormal.Z, 2); + pNormal.Normalize(); + collisionPoint.X = (float) Math.Round(collisionPoint.X, 1); + collisionPoint.Y = (float) Math.Round(collisionPoint.Y, 1); + collisionPoint.Z = (float) Math.Round(collisionPoint.Z, 1); + + Vector4 plane = new Vector4(pNormal.X, pNormal.Y, pNormal.Z, + Vector3.Dot(collisionPoint, pNormal)); + UpdateCameraCollisionPlane(plane); } } + else if (!m_pos.ApproxEquals(m_lastPosition, POSITION_TOLERANCE) || + !Rotation.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE)) + { + Vector4 plane = new Vector4(0.9f, 0.0f, 0.361f, -9000f); // not right... + UpdateCameraCollisionPlane(plane); + CameraConstraintActive = false; + } } /// @@ -1248,18 +1913,14 @@ namespace OpenSim.Region.Framework.Scenes { // m_log.DebugFormat( // "[SCENE PRESENCE]: In {0} received agent update from {1}, flags {2}", -// Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); +// Scene.Name, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); if (IsChildAgent) { - // // m_log.Debug("DEBUG: HandleAgentUpdate: child agent"); +// m_log.DebugFormat("DEBUG: HandleAgentUpdate: child agent in {0}", Scene.Name); return; } - ++m_movementUpdateCount; - if (m_movementUpdateCount < 1) - m_movementUpdateCount = 1; - #region Sanity Checking // This is irritating. Really. @@ -1290,36 +1951,20 @@ namespace OpenSim.Region.Framework.Scenes AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags; - // Camera location in world. We'll need to raytrace - // from this location from time to time. - CameraPosition = agentData.CameraCenter; - if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance) - { - ReprioritizeUpdates(); - m_lastCameraPosition = CameraPosition; - } - - // Use these three vectors to figure out what the agent is looking at - // Convert it to a Matrix and/or Quaternion - CameraAtAxis = agentData.CameraAtAxis; - CameraLeftAxis = agentData.CameraLeftAxis; - CameraUpAxis = agentData.CameraUpAxis; - // The Agent's Draw distance setting // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = agentData.Far; - DrawDistance = Scene.DefaultDrawDistance; - - // Check if Client has camera in 'follow cam' or 'build' mode. - Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation); - - m_followCamAuto = ((CameraUpAxis.Z > 0.959f && CameraUpAxis.Z < 0.98f) - && (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false; + DrawDistance = agentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; m_mouseLook = (flags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0; - m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0; + + // FIXME: This does not work as intended because the viewer only sends the lbutton down when the button + // is first pressed, not whilst it is held down. If this is required in the future then need to look + // for an AGENT_CONTROL_LBUTTON_UP event and make sure to handle cases where an initial DOWN is not + // received (e.g. on holding LMB down on the avatar in a viewer). +// m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0; #endregion Inputs @@ -1337,14 +1982,38 @@ namespace OpenSim.Region.Framework.Scenes StandUp(); } - //m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto); // Raycast from the avatar's head to the camera to see if there's anything blocking the view - if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast()) + // this exclude checks may not be complete + + if (m_movementUpdateCount % NumMovementsBetweenRayCast == 0 && m_scene.PhysicsScene.SupportsRayCast()) { - if (m_followCamAuto) + if (!m_doingCamRayCast && !m_mouseLook && ParentID == 0) { - Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT; - m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(CameraPosition - posAdjusted), Vector3.Distance(CameraPosition, posAdjusted) + 0.3f, RayCastCameraCallback); + Vector3 posAdjusted = AbsolutePosition; +// posAdjusted.Z += 0.5f * Appearance.AvatarSize.Z - 0.5f; + posAdjusted.Z += 1.0f; // viewer current camera focus point + Vector3 tocam = CameraPosition - posAdjusted; + tocam.X = (float)Math.Round(tocam.X, 1); + tocam.Y = (float)Math.Round(tocam.Y, 1); + tocam.Z = (float)Math.Round(tocam.Z, 1); + + float distTocamlen = tocam.Length(); + if (distTocamlen > 0.3f) + { + tocam *= (1.0f / distTocamlen); + posAdjusted.X = (float)Math.Round(posAdjusted.X, 1); + posAdjusted.Y = (float)Math.Round(posAdjusted.Y, 1); + posAdjusted.Z = (float)Math.Round(posAdjusted.Z, 1); + + m_doingCamRayCast = true; + m_scene.PhysicsScene.RaycastWorld(posAdjusted, tocam, distTocamlen + 1.0f, RayCastCameraCallback); + } + } + else if (CameraConstraintActive && (m_mouseLook || ParentID != 0)) + { + Vector4 plane = new Vector4(0.9f, 0.0f, 0.361f, -10000f); // not right... + UpdateCameraCollisionPlane(plane); + CameraConstraintActive = false; } } @@ -1358,9 +2027,16 @@ namespace OpenSim.Region.Framework.Scenes // Here's where you get them. m_AgentControlFlags = flags; m_headrotation = agentData.HeadRotation; + byte oldState = State; State = agentData.State; + // We need to send this back to the client in order to stop the edit beams + if ((oldState & (uint)AgentState.Editing) != 0 && State == (uint)AgentState.None) + ControllingClient.SendAgentTerseUpdate(this); + PhysicsActor actor = PhysicsActor; + + // This will be the case if the agent is sitting on the groudn or on an object. if (actor == null) { SendControlsToScripts(flagsForScripts); @@ -1369,17 +2045,26 @@ namespace OpenSim.Region.Framework.Scenes if (AllowMovement && !SitGround) { - Quaternion bodyRotation = agentData.BodyRotation; +// m_log.DebugFormat("[SCENE PRESENCE]: Initial body rotation {0} for {1}", agentData.BodyRotation, Name); + bool update_rotation = false; - if (bodyRotation != Rotation) + if (agentData.BodyRotation != Rotation) { - Rotation = bodyRotation; + Rotation = agentData.BodyRotation; update_rotation = true; } bool update_movementflag = false; + // If we were just made root agent then we must perform movement updates for the first AgentUpdate that + // we get + if (MovementFlag == ForceUpdateMovementFlagValue) + { + MovementFlag = 0; + update_movementflag = true; + } + if (agentData.UseClientAgentPosition) { MovingToTarget = (agentData.ClientAgentPosition - AbsolutePosition).Length() > 0.2f; @@ -1411,19 +2096,7 @@ namespace OpenSim.Region.Framework.Scenes { bool bAllowUpdateMoveToPosition = false; - Vector3[] dirVectors; - - // use camera up angle when in mouselook and not flying or when holding the left mouse button down and not flying - // this prevents 'jumping' in inappropriate situations. - if (!Flying && (m_mouseLook || m_leftButtonDown)) - dirVectors = GetWalkDirectionVectors(); - else - dirVectors = Dir_Vectors; - - // The fact that MovementFlag is a byte needs to be fixed - // it really should be a uint // A DIR_CONTROL_FLAG occurs when the user is trying to move in a particular direction. - uint nudgehack = 250; foreach (Dir_ControlFlags DCF in DIR_CONTROL_FLAGS) { if (((uint)flags & (uint)DCF) != 0) @@ -1432,7 +2105,9 @@ namespace OpenSim.Region.Framework.Scenes try { - agent_control_v3 += dirVectors[i]; + // Don't slide against ground when crouching if camera is panned around avatar + if (Flying || DCF != Dir_ControlFlags.DIR_CONTROL_FLAG_DOWN) + agent_control_v3 += Dir_Vectors[i]; //m_log.DebugFormat("[Motion]: {0}, {1}",i, dirVectors[i]); } catch (IndexOutOfRangeException) @@ -1440,29 +2115,19 @@ namespace OpenSim.Region.Framework.Scenes // Why did I get this? } - if ((MovementFlag & (byte)(uint)DCF) == 0) - { - if (DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_BACKWARD_NUDGE || - DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_LEFT_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_RIGHT_NUDGE) - { - MovementFlag |= (byte)nudgehack; - } - + if (((MovementFlag & (uint)DCF) == 0) & !AgentControlStopActive) + { //m_log.DebugFormat("[SCENE PRESENCE]: Updating MovementFlag for {0} with {1}", Name, DCF); - MovementFlag += (byte)(uint)DCF; + MovementFlag += (uint)DCF; update_movementflag = true; } } else { - if ((MovementFlag & (byte)(uint)DCF) != 0 || - ((DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_BACKWARD_NUDGE || - DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_LEFT_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_RIGHT_NUDGE) - && ((MovementFlag & (byte)nudgehack) == nudgehack)) - ) // This or is for Nudge forward + if ((MovementFlag & (uint)DCF) != 0) { //m_log.DebugFormat("[SCENE PRESENCE]: Updating MovementFlag for {0} with lack of {1}", Name, DCF); - MovementFlag -= ((byte)(uint)DCF); + MovementFlag -= (uint)DCF; update_movementflag = true; /* @@ -1482,6 +2147,13 @@ namespace OpenSim.Region.Framework.Scenes i++; } + // Detect AGENT_CONTROL_STOP state changes + if (AgentControlStopActive != ((flags & AgentManager.ControlFlags.AGENT_CONTROL_STOP) != 0)) + { + AgentControlStopActive = !AgentControlStopActive; + update_movementflag = true; + } + if (MovingToTarget) { // If the user has pressed a key then we want to cancel any move to target. @@ -1507,30 +2179,79 @@ namespace OpenSim.Region.Framework.Scenes // Only do this if we're flying if (Flying && !ForceFly) { - // Landing detection code + // Need to stop in mid air if user holds down AGENT_CONTROL_STOP + if (AgentControlStopActive) + { + agent_control_v3 = Vector3.Zero; + } + else + { + // Landing detection code - // Are the landing controls requirements filled? - bool controlland = (((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || - ((flags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); + // Are the landing controls requirements filled? + bool controlland = (((flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0) || + ((flags & AgentManager.ControlFlags.AGENT_CONTROL_NUDGE_UP_NEG) != 0)); - if (Flying && IsColliding && controlland) - { - // nesting this check because LengthSquared() is expensive and we don't - // want to do it every step when flying. - if ((Velocity.LengthSquared() <= LAND_VELOCITYMAG_MAX)) - StopFlying(); + //m_log.Debug("[CONTROL]: " +flags); + // Applies a satisfying roll effect to the avatar when flying. + if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) != 0 && (flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_POS) != 0) + { + ApplyFlyingRoll( + FLY_ROLL_RADIANS_PER_UPDATE, + (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0, + (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0); + } + else if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) != 0 && + (flags & AgentManager.ControlFlags.AGENT_CONTROL_YAW_NEG) != 0) + { + ApplyFlyingRoll( + -FLY_ROLL_RADIANS_PER_UPDATE, + (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) != 0, + (flags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) != 0); + } + else + { + if (m_AngularVelocity.Z != 0) + m_AngularVelocity.Z += CalculateFlyingRollResetToZero(FLY_ROLL_RESET_RADIANS_PER_UPDATE); + } + + if (Flying && IsColliding && controlland) + { + // nesting this check because LengthSquared() is expensive and we don't + // want to do it every step when flying. + if ((Velocity.LengthSquared() <= LAND_VELOCITYMAG_MAX)) + StopFlying(); + } } } +// m_log.DebugFormat("[SCENE PRESENCE]: MovementFlag {0} for {1}", MovementFlag, Name); + // If the agent update does move the avatar, then calculate the force ready for the velocity update, // which occurs later in the main scene loop - if (update_movementflag || (update_rotation && DCFlagKeyPressed)) + // We also need to update if the user rotates their avatar whilst it is slow walking/running (if they + // held down AGENT_CONTROL_STOP whilst normal walking/running). However, we do not want to update + // if the user rotated whilst holding down AGENT_CONTROL_STOP when already still (which locks the + // avatar location in place). + if (update_movementflag + || (update_rotation && DCFlagKeyPressed && (!AgentControlStopActive || MovementFlag != 0))) { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: In {0} adding velocity of {1} to {2}, umf = {3}, ur = {4}", -// m_scene.RegionInfo.RegionName, agent_control_v3, Name, update_movementflag, update_rotation); +// if (update_movementflag || !AgentControlStopActive || MovementFlag != 0) +// { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: In {0} adding velocity of {1} to {2}, umf = {3}, mf = {4}, ur = {5}", +// m_scene.RegionInfo.RegionName, agent_control_v3, Name, +// update_movementflag, MovementFlag, update_rotation); + + float speedModifier; + + if (AgentControlStopActive) + speedModifier = AgentControlStopSlowWhilstMoving; + else + speedModifier = 1; - AddNewMovement(agent_control_v3); + AddNewMovement(agent_control_v3, speedModifier); +// } } // else // { @@ -1543,15 +2264,86 @@ namespace OpenSim.Region.Framework.Scenes // } if (update_movementflag && ParentID == 0) + { +// m_log.DebugFormat("[SCENE PRESENCE]: Updating movement animations for {0}", Name); Animator.UpdateMovementAnimations(); + } SendControlsToScripts(flagsForScripts); } + // We need to send this back to the client in order to see the edit beams + if ((State & (uint)AgentState.Editing) != 0) + ControllingClient.SendAgentTerseUpdate(this); + m_scene.EventManager.TriggerOnClientMovement(this); - TriggerScenePresenceUpdated(); } + + /// + /// This is the event handler for client cameras. If a client is moving, or moving the camera, this event is triggering. + /// + private void HandleAgentCamerasUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) + { + //m_log.DebugFormat( + // "[SCENE PRESENCE]: In {0} received agent camera update from {1}, flags {2}", + // Scene.RegionInfo.RegionName, remoteClient.Name, (AgentManager.ControlFlags)agentData.ControlFlags); + + if (IsChildAgent) + { + // // m_log.Debug("DEBUG: HandleAgentUpdate: child agent"); + return; + } + + ++m_movementUpdateCount; + if (m_movementUpdateCount < 1) + m_movementUpdateCount = 1; + +// AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags; + + // Camera location in world. We'll need to raytrace + // from this location from time to time. + CameraPosition = agentData.CameraCenter; + if (Vector3.Distance(m_lastCameraPosition, CameraPosition) >= Scene.RootReprioritizationDistance) + { + ReprioritizeUpdates(); + m_lastCameraPosition = CameraPosition; + } + + // Use these three vectors to figure out what the agent is looking at + // Convert it to a Matrix and/or Quaternion + CameraAtAxis = agentData.CameraAtAxis; + CameraLeftAxis = agentData.CameraLeftAxis; + CameraUpAxis = agentData.CameraUpAxis; + + // The Agent's Draw distance setting + // When we get to the point of re-computing neighbors everytime this + // changes, then start using the agent's drawdistance rather than the + // region's draw distance. + DrawDistance = agentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; + + // Check if Client has camera in 'follow cam' or 'build' mode. + Vector3 camdif = (Vector3.One * Rotation - Vector3.One * CameraRotation); + + m_followCamAuto = ((CameraUpAxis.Z > 0.959f && CameraUpAxis.Z < 0.98f) + && (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false; + + + //m_log.DebugFormat("[FollowCam]: {0}", m_followCamAuto); + // Raycast from the avatar's head to the camera to see if there's anything blocking the view + if ((m_movementUpdateCount % NumMovementsBetweenRayCast) == 0 && m_scene.PhysicsScene.SupportsRayCast()) + { + if (m_followCamAuto) + { + Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT; + m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(CameraPosition - posAdjusted), Vector3.Distance(CameraPosition, posAdjusted) + 0.3f, RayCastCameraCallback); + } + } + + TriggerScenePresenceUpdated(); + } + /// /// Calculate an update to move the presence to the set target. /// @@ -1706,13 +2498,15 @@ namespace OpenSim.Region.Framework.Scenes if (regionCombinerModule != null) regionSize = regionCombinerModule.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID); else - regionSize = new Vector2(Constants.RegionSize); + regionSize = new Vector2(m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY); if (pos.X < 0 || pos.X >= regionSize.X || pos.Y < 0 || pos.Y >= regionSize.Y || pos.Z < 0) return; + Scene targetScene = m_scene; + // Vector3 heightAdjust = new Vector3(0, 0, Appearance.AvatarHeight / 2); // pos += heightAdjust; // @@ -1724,15 +2518,23 @@ namespace OpenSim.Region.Framework.Scenes // } // Get terrain height for sub-region in a megaregion if necessary - int X = (int)((m_scene.RegionInfo.RegionLocX * Constants.RegionSize) + pos.X); - int Y = (int)((m_scene.RegionInfo.RegionLocY * Constants.RegionSize) + pos.Y); - UUID target_regionID = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, X, Y).RegionID; - Scene targetScene = m_scene; - if (!SceneManager.Instance.TryGetScene(target_regionID, out targetScene)) - targetScene = m_scene; + //COMMENT: If its only nessesary in a megaregion, why do it on normal region's too? + + if (regionCombinerModule != null) + { + int x = (int)((m_scene.RegionInfo.WorldLocX) + pos.X); + int y = (int)((m_scene.RegionInfo.WorldLocY) + pos.Y); + GridRegion target_region = m_scene.GridService.GetRegionByPosition(m_scene.RegionInfo.ScopeID, x, y); + + // If X and Y is NaN, target_region will be null + if (target_region == null) + return; + + SceneManager.Instance.TryGetScene(target_region.RegionID, out targetScene); + } - float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % Constants.RegionSize), (int)(pos.Y % Constants.RegionSize)]; + float terrainHeight = (float)targetScene.Heightmap[(int)(pos.X % regionSize.X), (int)(pos.Y % regionSize.Y)]; pos.Z = Math.Max(terrainHeight, pos.Z); // Fudge factor. It appears that if one clicks "go here" on a piece of ground, the go here request is @@ -1741,15 +2543,18 @@ namespace OpenSim.Region.Framework.Scenes if (pos.Z - terrainHeight < 0.2) pos.Z = terrainHeight; -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Avatar {0} set move to target {1} (terrain height {2}) in {3}", -// Name, pos, terrainHeight, m_scene.RegionInfo.RegionName); - if (noFly) Flying = false; else if (pos.Z > terrainHeight) Flying = true; +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Avatar {0} set move to target {1} (terrain height {2}) in {3}", +// Name, pos, terrainHeight, m_scene.RegionInfo.RegionName); + + if (noFly) + Flying = false; + LandAtTarget = landAtTarget; MovingToTarget = true; MoveToPositionTarget = pos; @@ -1782,7 +2587,8 @@ namespace OpenSim.Region.Framework.Scenes // m_log.DebugFormat("[SCENE PRESENCE]: Resetting move to target for {0}", Name); MovingToTarget = false; - MoveToPositionTarget = Vector3.Zero; +// MoveToPositionTarget = Vector3.Zero; + m_forceToApply = null; // cancel possible last action // We need to reset the control flag as the ScenePresenceAnimator uses this to determine the correct // resting animation (e.g. hover or stand). NPCs don't have a client that will quickly reset this flag. @@ -1799,13 +2605,15 @@ namespace OpenSim.Region.Framework.Scenes { // m_log.DebugFormat("[SCENE PRESENCE]: StandUp() for {0}", Name); + bool satOnObject = IsSatOnObject; + SceneObjectPart part = ParentPart; SitGround = false; - if (PhysicsActor == null) - AddToPhysicalScene(false); - if (ParentID != 0) + if (satOnObject) { - SceneObjectPart part = ParentPart; + PrevSitOffset = m_pos; // Save sit offset + UnRegisterSeatControls(part.ParentGroup.UUID); + TaskInventoryDictionary taskIDict = part.TaskInventory; if (taskIDict != null) { @@ -1821,24 +2629,70 @@ namespace OpenSim.Region.Framework.Scenes } } - ParentPosition = part.GetWorldPosition(); ControllingClient.SendClearFollowCamProperties(part.ParentUUID); - m_pos += ParentPosition + new Vector3(0.0f, 0.0f, 2.0f * m_sitAvatarHeight); - ParentPosition = Vector3.Zero; - ParentID = 0; ParentPart = null; - SendAvatarDataToAllAgents(); + + Quaternion standRotation; + + if (part.SitTargetAvatar == UUID) + { + standRotation = part.GetWorldRotation(); + + if (!part.IsRoot) + standRotation = standRotation * part.SitTargetOrientation; +// standRotation = part.RotationOffset * part.SitTargetOrientation; +// else +// standRotation = part.SitTargetOrientation; + + } + else + { + standRotation = Rotation; + } + + //Vector3 standPos = ParentPosition + new Vector3(0.0f, 0.0f, 2.0f * m_sitAvatarHeight); + //Vector3 standPos = ParentPosition; + +// Vector3 standPositionAdjustment +// = part.SitTargetPosition + new Vector3(0.5f, 0f, m_sitAvatarHeight / 2f); + Vector3 adjustmentForSitPosition = OffsetPosition * part.ParentGroup.GroupRotation - SIT_TARGET_ADJUSTMENT * part.GetWorldRotation(); + + // XXX: This is based on the physics capsule sizes. Need to find a better way to read this rather than + // hardcoding here. + Vector3 adjustmentForSitPose = new Vector3(0.74f, 0f, 0f) * standRotation; + + Vector3 standPos = part.ParentGroup.AbsolutePosition + adjustmentForSitPosition + adjustmentForSitPose; + +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Setting stand to pos {0}, (adjustmentForSitPosition {1}, adjustmentForSitPose {2}) rotation {3} for {4} in {5}", +// standPos, adjustmentForSitPosition, adjustmentForSitPose, standRotation, Name, Scene.Name); + + Rotation = standRotation; + AbsolutePosition = standPos; + } + + // We need to wait until we have calculated proper stand positions before sitting up the physical + // avatar to avoid race conditions. + if (PhysicsActor == null) + AddToPhysicalScene(false); + + if (satOnObject) + { + SendAvatarDataToAllClients(); m_requestedSitTargetID = 0; - part.RemoveSittingAvatar(UUID); + part.RemoveSittingAvatar(this); - if (part != null) - part.ParentGroup.TriggerScriptChangedEvent(Changed.LINK); + part.ParentGroup.TriggerScriptChangedEvent(Changed.LINK); } + else if (PhysicsActor == null) + AddToPhysicalScene(false); + Animator.TrySetMovementAnimation("STAND"); + TriggerScenePresenceUpdated(); } private SceneObjectPart FindNextAvailableSitTarget(UUID targetID) @@ -1885,14 +2739,10 @@ namespace OpenSim.Region.Framework.Scenes if (part == null) return; - // TODO: determine position to sit at based on scene geometry; don't trust offset from client - // see http://wiki.secondlife.com/wiki/User:Andrew_Linden/Office_Hours/2007_11_06 for details on how LL does it - if (PhysicsActor != null) - m_sitAvatarHeight = PhysicsActor.Size.Z; + m_sitAvatarHeight = PhysicsActor.Size.Z * 0.5f; bool canSit = false; - Vector3 pos = part.AbsolutePosition + offset; if (part.IsSitTargetSet && part.SitTargetAvatar == UUID.Zero) { @@ -1902,45 +2752,88 @@ namespace OpenSim.Region.Framework.Scenes offset = part.SitTargetPosition; sitOrientation = part.SitTargetOrientation; + + if (!part.IsRoot) + { + // m_log.DebugFormat("Old sit orient {0}", sitOrientation); + sitOrientation = part.RotationOffset * sitOrientation; + // m_log.DebugFormat("New sit orient {0}", sitOrientation); +// m_log.DebugFormat("Old sit offset {0}", offset); + offset = offset * part.RotationOffset; +// m_log.DebugFormat("New sit offset {0}", offset); + } + canSit = true; } else { + if (PhysicsSit(part,offset)) // physics engine + return; + + Vector3 pos = part.AbsolutePosition + offset; + if (Util.GetDistanceTo(AbsolutePosition, pos) <= 10) { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Sitting {0} on {1} {2} because sit target is unset and within 10m", -// Name, part.Name, part.LocalId); - AbsolutePosition = pos + new Vector3(0.0f, 0.0f, m_sitAvatarHeight); canSit = true; } -// else -// { -// m_log.DebugFormat( -// "[SCENE PRESENCE]: Ignoring sit request of {0} on {1} {2} because sit target is unset and outside 10m", -// Name, part.Name, part.LocalId); -// } } if (canSit) { + if (PhysicsActor != null) { // We can remove the physicsActor until they stand up. RemoveFromPhysicalScene(); } - part.AddSittingAvatar(UUID); + if (MovingToTarget) + ResetMoveToTarget(); + + Velocity = Vector3.Zero; + + part.AddSittingAvatar(this); cameraAtOffset = part.GetCameraAtOffset(); + + if (!part.IsRoot && cameraAtOffset == Vector3.Zero) + cameraAtOffset = part.ParentGroup.RootPart.GetCameraAtOffset(); + + bool cameraEyeOffsetFromRootForChild = false; cameraEyeOffset = part.GetCameraEyeOffset(); + + if (!part.IsRoot && cameraEyeOffset == Vector3.Zero) + { + cameraEyeOffset = part.ParentGroup.RootPart.GetCameraEyeOffset(); + cameraEyeOffsetFromRootForChild = true; + } + + if ((cameraEyeOffset != Vector3.Zero && !cameraEyeOffsetFromRootForChild) || cameraAtOffset != Vector3.Zero) + { + if (!part.IsRoot) + { + cameraEyeOffset = cameraEyeOffset * part.RotationOffset; + cameraAtOffset += part.OffsetPosition; + } + + cameraEyeOffset += part.OffsetPosition; + } + +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Using cameraAtOffset {0}, cameraEyeOffset {1} for sit on {2} by {3} in {4}", +// cameraAtOffset, cameraEyeOffset, part.Name, Name, Scene.Name); + forceMouselook = part.GetForceMouselook(); + // An viewer expects to specify sit positions as offsets to the root prim, even if a child prim is + // being sat upon. + offset += part.OffsetPosition; + ControllingClient.SendSitResponse( - targetID, offset, sitOrientation, false, cameraAtOffset, cameraEyeOffset, forceMouselook); + part.ParentGroup.UUID, offset, sitOrientation, false, cameraAtOffset, cameraEyeOffset, forceMouselook); - m_requestedSitTargetUUID = targetID; + m_requestedSitTargetUUID = part.UUID; HandleAgentSit(ControllingClient, UUID); @@ -1952,6 +2845,9 @@ namespace OpenSim.Region.Framework.Scenes public void HandleAgentRequestSit(IClientAPI remoteClient, UUID agentID, UUID targetID, Vector3 offset) { + if (IsChildAgent) + return; + if (ParentID != 0) { if (ParentPart.UUID == targetID) @@ -1965,16 +2861,8 @@ namespace OpenSim.Region.Framework.Scenes if (part != null) { m_requestedSitTargetID = part.LocalId; - m_requestedSitTargetUUID = targetID; + m_requestedSitTargetUUID = part.UUID; -// m_log.DebugFormat("[SIT]: Client requested Sit Position: {0}", offset); - - if (m_scene.PhysicsScene.SupportsRayCast()) - { - //m_scene.PhysicsScene.RaycastWorld(Vector3.Zero,Vector3.Zero, 0.01f,new RaycastCallback()); - //SitRayCastAvatarPosition(part); - //return; - } } else { @@ -1984,200 +2872,119 @@ namespace OpenSim.Region.Framework.Scenes SendSitResponse(targetID, offset, Quaternion.Identity); } - /* - public void SitRayCastAvatarPosition(SceneObjectPart part) - { - Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset; - Vector3 StartRayCastPosition = AbsolutePosition; - Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition); - float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition); - m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastAvatarPositionResponse); - } - - public void SitRayCastAvatarPositionResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) + // returns false if does not suport so older sit can be tried + public bool PhysicsSit(SceneObjectPart part, Vector3 offset) { - SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); - if (part != null) - { - if (hitYN) - { - if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f)) - { - SitRaycastFindEdge(collisionPoint, normal); - m_log.DebugFormat("[SIT]: Raycast Avatar Position succeeded at point: {0}, normal:{1}", collisionPoint, normal); - } - else - { - SitRayCastAvatarPositionCameraZ(part); - } - } - else - { - SitRayCastAvatarPositionCameraZ(part); - } - } - else +// TODO: Pull in these bits + return false; +/* + if (part == null || part.ParentGroup.IsAttachment) { - ControllingClient.SendAlertMessage("Sit position no longer exists"); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; + return true; } - } + if ( m_scene.PhysicsScene == null) + return false; - public void SitRayCastAvatarPositionCameraZ(SceneObjectPart part) - { - // Next, try to raycast from the camera Z position - Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset; - Vector3 StartRayCastPosition = AbsolutePosition; StartRayCastPosition.Z = CameraPosition.Z; - Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition); - float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition); - m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastAvatarPositionCameraZResponse); - } - - public void SitRayCastAvatarPositionCameraZResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) - { - SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); - if (part != null) + if (part.PhysActor == null) { - if (hitYN) - { - if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f)) - { - SitRaycastFindEdge(collisionPoint, normal); - m_log.DebugFormat("[SIT]: Raycast Avatar Position + CameraZ succeeded at point: {0}, normal:{1}", collisionPoint, normal); - } - else - { - SitRayCastCameraPosition(part); - } - } + // none physcis shape + if (part.PhysicsShapeType == (byte)PhysicsShapeType.None) + ControllingClient.SendAlertMessage(" There is no suitable surface to sit on, try another spot."); else - { - SitRayCastCameraPosition(part); + { // non physical phantom TODO + ControllingClient.SendAlertMessage(" There is no suitable surface to sit on, try another spot."); + return false; } - } - else - { - ControllingClient.SendAlertMessage("Sit position no longer exists"); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; + return true; } - } - public void SitRayCastCameraPosition(SceneObjectPart part) - { - // Next, try to raycast from the camera position - Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset; - Vector3 StartRayCastPosition = CameraPosition; - Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition); - float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition); - m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastCameraPositionResponse); - } + // not doing autopilot + m_requestedSitTargetID = 0; - public void SitRayCastCameraPositionResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) - { - SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); - if (part != null) - { - if (hitYN) - { - if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f)) - { - SitRaycastFindEdge(collisionPoint, normal); - m_log.DebugFormat("[SIT]: Raycast Camera Position succeeded at point: {0}, normal:{1}", collisionPoint, normal); - } - else - { - SitRayHorizontal(part); - } - } - else - { - SitRayHorizontal(part); - } - } - else - { - ControllingClient.SendAlertMessage("Sit position no longer exists"); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; - } + if (m_scene.PhysicsScene.SitAvatar(part.PhysActor, AbsolutePosition, CameraPosition, offset, new Vector3(0.35f, 0, 0.65f), PhysicsSitResponse) != 0) + return true; + return false; +*/ } - public void SitRayHorizontal(SceneObjectPart part) + + private bool CanEnterLandPosition(Vector3 testPos) { - // Next, try to raycast from the avatar position to fwd - Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset; - Vector3 StartRayCastPosition = CameraPosition; - Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition); - float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition); - m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastHorizontalResponse); + ILandObject land = m_scene.LandChannel.GetLandObject(testPos.X, testPos.Y); + + if (land == null || land.LandData.Name == "NO_LAND") + return true; + + return land.CanBeOnThisLand(UUID,testPos.Z); } - public void SitRayCastHorizontalResponse(bool hitYN, Vector3 collisionPoint, uint localid, float pdistance, Vector3 normal) + // status + // < 0 ignore + // 0 bad sit spot + public void PhysicsSitResponse(int status, uint partID, Vector3 offset, Quaternion Orientation) { - SceneObjectPart part = FindNextAvailableSitTarget(m_requestedSitTargetUUID); - if (part != null) + if (status < 0) + return; + + if (status == 0) { - if (hitYN) - { - if (collisionPoint.ApproxEquals(m_requestedSitOffset + part.AbsolutePosition, 0.2f)) - { - SitRaycastFindEdge(collisionPoint, normal); - m_log.DebugFormat("[SIT]: Raycast Horizontal Position succeeded at point: {0}, normal:{1}", collisionPoint, normal); - // Next, try to raycast from the camera position - Vector3 EndRayCastPosition = part.AbsolutePosition + m_requestedSitOffset; - Vector3 StartRayCastPosition = CameraPosition; - Vector3 direction = Vector3.Normalize(EndRayCastPosition - StartRayCastPosition); - float distance = Vector3.Distance(EndRayCastPosition, StartRayCastPosition); - //m_scene.PhysicsScene.RaycastWorld(StartRayCastPosition, direction, distance, SitRayCastResponseAvatarPosition); - } - else - { - ControllingClient.SendAlertMessage("Sit position not accessable."); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; - } - } - else - { - ControllingClient.SendAlertMessage("Sit position not accessable."); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; - } + ControllingClient.SendAlertMessage(" There is no suitable surface to sit on, try another spot."); + return; } - else + + SceneObjectPart part = m_scene.GetSceneObjectPart(partID); + if (part == null) + return; + + Vector3 targetPos = part.GetWorldPosition() + offset * part.GetWorldRotation(); + if(!CanEnterLandPosition(targetPos)) { - ControllingClient.SendAlertMessage("Sit position no longer exists"); - m_requestedSitTargetUUID = UUID.Zero; - m_requestedSitTargetID = 0; - m_requestedSitOffset = Vector3.Zero; + ControllingClient.SendAlertMessage(" Sit position on restricted land, try another spot"); + return; } - } + RemoveFromPhysicalScene(); - private void SitRaycastFindEdge(Vector3 collisionPoint, Vector3 collisionNormal) - { - int i = 0; - //throw new NotImplementedException(); - //m_requestedSitTargetUUID = UUID.Zero; - //m_requestedSitTargetID = 0; - //m_requestedSitOffset = Vector3.Zero; + if (MovingToTarget) + ResetMoveToTarget(); + + Velocity = Vector3.Zero; + + part.AddSittingAvatar(this); + + Vector3 cameraAtOffset = part.GetCameraAtOffset(); + Vector3 cameraEyeOffset = part.GetCameraEyeOffset(); + bool forceMouselook = part.GetForceMouselook(); + + ControllingClient.SendSitResponse( + part.UUID, offset, Orientation, false, cameraAtOffset, cameraEyeOffset, forceMouselook); + + // not using autopilot + + Rotation = Orientation; + m_pos = offset; + + m_requestedSitTargetID = 0; + + ParentPart = part; + ParentID = part.LocalId; + if(status == 3) + Animator.TrySetMovementAnimation("SIT_GROUND"); + else + Animator.TrySetMovementAnimation("SIT"); + SendAvatarDataToAllClients(); - SendSitResponse(ControllingClient, m_requestedSitTargetUUID, collisionPoint - m_requestedSitOffset, Quaternion.Identity); + part.ParentGroup.TriggerScriptChangedEvent(Changed.LINK); } - */ public void HandleAgentSit(IClientAPI remoteClient, UUID agentID) { + if (IsChildAgent) + return; + SceneObjectPart part = m_scene.GetSceneObjectPart(m_requestedSitTargetID); if (part != null) @@ -2205,23 +3012,75 @@ namespace OpenSim.Region.Framework.Scenes //Quaternion result = (sitTargetOrient * vq) * nq; - m_pos = sitTargetPos + SIT_TARGET_ADJUSTMENT; - Rotation = sitTargetOrient; - ParentPosition = part.AbsolutePosition; + double x, y, z, m1, m2; + + Quaternion r = sitTargetOrient; + m1 = r.X * r.X + r.Y * r.Y; + m2 = r.Z * r.Z + r.W * r.W; + + // Rotate the vector <0, 0, 1> + x = 2 * (r.X * r.Z + r.Y * r.W); + y = 2 * (-r.X * r.W + r.Y * r.Z); + z = m2 - m1; + + // Set m to be the square of the norm of r. + double m = m1 + m2; + + // This constant is emperically determined to be what is used in SL. + // See also http://opensimulator.org/mantis/view.php?id=7096 + double offset = 0.05; + + // Normally m will be ~ 1, but if someone passed a handcrafted quaternion + // to llSitTarget with values so small that squaring them is rounded off + // to zero, then m could be zero. The result of this floating point + // round off error (causing us to skip this impossible normalization) + // is only 5 cm. + if (m > 0.000001) + { + offset /= m; + } + + Vector3 up = new Vector3((float)x, (float)y, (float)z); + Vector3 sitOffset = up * (float)offset; + + // sitOffset is in Avatar Center coordinates: from origin to 'sitTargetPos + SIT_TARGET_ADJUSTMENT'. + // So, we need to _substract_ it to get to the origin of the Avatar Center. + Vector3 newPos = sitTargetPos + SIT_TARGET_ADJUSTMENT - sitOffset; + Quaternion newRot; + + if (part.IsRoot) + { + newRot = sitTargetOrient; + } + else + { + newPos = newPos * part.RotationOffset; + newRot = part.RotationOffset * sitTargetOrient; + } + + newPos += part.OffsetPosition; + + m_pos = newPos; + Rotation = newRot; + +// ParentPosition = part.AbsolutePosition; } else { - m_pos -= part.AbsolutePosition; - ParentPosition = part.AbsolutePosition; + // An viewer expects to specify sit positions as offsets to the root prim, even if a child prim is + // being sat upon. + m_pos -= part.GroupPosition; + +// ParentPosition = part.AbsolutePosition; // m_log.DebugFormat( // "[SCENE PRESENCE]: Sitting {0} at position {1} ({2} + {3}) on part {4} {5} without sit target", // Name, part.AbsolutePosition, m_pos, ParentPosition, part.Name, part.LocalId); } - ParentPart = m_scene.GetSceneObjectPart(m_requestedSitTargetID); + ParentPart = part; ParentID = m_requestedSitTargetID; - + m_AngularVelocity = Vector3.Zero; Velocity = Vector3.Zero; RemoveFromPhysicalScene(); @@ -2231,14 +3090,20 @@ namespace OpenSim.Region.Framework.Scenes sitAnimation = part.SitAnimation; } Animator.TrySetMovementAnimation(sitAnimation); - SendAvatarDataToAllAgents(); + SendAvatarDataToAllClients(); + TriggerScenePresenceUpdated(); } } public void HandleAgentSitOnGround() { -// m_updateCount = 0; // Kill animation update burst so that the SIT_G.. will stick. + if (IsChildAgent) + return; + +// m_updateCount = 0; // Kill animation update burst so that the SIT_G.. will stick.. + m_AngularVelocity = Vector3.Zero; Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED"); + TriggerScenePresenceUpdated(); SitGround = true; RemoveFromPhysicalScene(); } @@ -2255,22 +3120,39 @@ namespace OpenSim.Region.Framework.Scenes public void HandleStartAnim(IClientAPI remoteClient, UUID animID) { Animator.AddAnimation(animID, UUID.Zero); + TriggerScenePresenceUpdated(); } public void HandleStopAnim(IClientAPI remoteClient, UUID animID) { Animator.RemoveAnimation(animID, false); + TriggerScenePresenceUpdated(); } /// /// Rotate the avatar to the given rotation and apply a movement in the given relative vector /// /// The vector in which to move. This is relative to the rotation argument - public void AddNewMovement(Vector3 vec) + /// + /// Optional additional speed modifier for this particular add. Default is 1 + public void AddNewMovement(Vector3 vec, float thisAddSpeedModifier = 1) { -// m_log.DebugFormat("[SCENE PRESENCE]: Adding new movement {0} for {1}", vec, Name); +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Adding new movement {0} with rotation {1}, thisAddSpeedModifier {2} for {3}", +// vec, Rotation, thisAddSpeedModifier, Name); - Vector3 direc = vec * Rotation; + Quaternion rot = Rotation; + if (!Flying && PresenceType != PresenceType.Npc) + { + // The only situation in which we care about X and Y is avatar flying. The rest of the time + // these parameters are not relevant for determining avatar movement direction and cause issues such + // as wrong walk speed if the camera is rotated. + rot.X = 0; + rot.Y = 0; + rot.Normalize(); + } + + Vector3 direc = vec * rot; direc.Normalize(); if (Flying != FlyingOld) // add for fly velocity control @@ -2286,7 +3168,9 @@ namespace OpenSim.Region.Framework.Scenes if ((vec.Z == 0f) && !Flying) direc.Z = 0f; // Prevent camera WASD up. - direc *= 0.03f * 128f * SpeedModifier; + direc *= 0.03f * 128f * SpeedModifier * thisAddSpeedModifier; + +// m_log.DebugFormat("[SCENE PRESENCE]: Force to apply before modification was {0} for {1}", direc, Name); if (PhysicsActor != null) { @@ -2315,14 +3199,17 @@ namespace OpenSim.Region.Framework.Scenes direc.Z *= 2.6f; // TODO: PreJump and jump happen too quickly. Many times prejump gets ignored. - Animator.TrySetMovementAnimation("PREJUMP"); - Animator.TrySetMovementAnimation("JUMP"); +// Animator.TrySetMovementAnimation("PREJUMP"); +// Animator.TrySetMovementAnimation("JUMP"); } } } +// m_log.DebugFormat("[SCENE PRESENCE]: Setting force to apply to {0} for {1}", direc, Name); + // TODO: Add the force instead of only setting it to support multiple forces per frame? m_forceToApply = direc; + Animator.UpdateMovementAnimations(); } #endregion @@ -2331,25 +3218,27 @@ namespace OpenSim.Region.Framework.Scenes public override void Update() { - const float ROTATION_TOLERANCE = 0.01f; - const float VELOCITY_TOLERANCE = 0.001f; - const float POSITION_TOLERANCE = 0.05f; - if (IsChildAgent == false) { // NOTE: Velocity is not the same as m_velocity. Velocity will attempt to // grab the latest PhysicsActor velocity, whereas m_velocity is often // storing a requested force instead of an actual traveling velocity - - // Throw away duplicate or insignificant updates - if ( - // If the velocity has become zero, send it no matter what. - (Velocity != m_lastVelocity && Velocity == Vector3.Zero) - // otherwise, if things have changed reasonably, send the update - || (!Rotation.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE) - || !Velocity.ApproxEquals(m_lastVelocity, VELOCITY_TOLERANCE) - || !m_pos.ApproxEquals(m_lastPosition, POSITION_TOLERANCE))) - + if (Appearance.AvatarSize != m_lastSize && !IsLoggingIn) + SendAvatarDataToAllClients(); + + // Allow any updates for sitting avatars to that llSetPrimitiveLinkParams() can work for very + // small increments (e.g. sit position adjusters). An alternative may be to eliminate the tolerance + // checks on all updates but the ramifications of this would need careful consideration. + bool updateClients + = IsSatOnObject && (Rotation != m_lastRotation || Velocity != m_lastVelocity || m_pos != m_lastPosition); + + if (!updateClients) + updateClients + = !Rotation.ApproxEquals(m_lastRotation, Scene.RootRotationUpdateTolerance) + || !Velocity.ApproxEquals(m_lastVelocity, Scene.RootVelocityUpdateTolerance) + || !m_pos.ApproxEquals(m_lastPosition, Scene.RootPositionUpdateTolerance); + + if (updateClients) { SendTerseUpdateToAllClients(); @@ -2359,7 +3248,8 @@ namespace OpenSim.Region.Framework.Scenes m_lastVelocity = Velocity; } - CheckForBorderCrossing(); + if (Scene.AllowAvatarCrossing) + CheckForBorderCrossing(); CheckForSignificantMovement(); // sends update to the modules. } @@ -2369,7 +3259,6 @@ namespace OpenSim.Region.Framework.Scenes #region Update Client(s) - /// /// Sends a location update to the client connected to this scenePresence /// @@ -2380,6 +3269,29 @@ namespace OpenSim.Region.Framework.Scenes // server. if (remoteClient.IsActive) { + if (Scene.RootTerseUpdatePeriod > 1) + { +// Console.WriteLine( +// "{0} {1} {2} {3} {4} {5} for {6} to {7}", +// remoteClient.AgentId, UUID, remoteClient.SceneAgent.IsChildAgent, m_terseUpdateCount, Scene.RootTerseUpdatePeriod, Velocity.ApproxEquals(Vector3.Zero, 0.001f), Name, remoteClient.Name); + if (remoteClient.AgentId != UUID + && !remoteClient.SceneAgent.IsChildAgent + && m_terseUpdateCount % Scene.RootTerseUpdatePeriod != 0 + && !Velocity.ApproxEquals(Vector3.Zero, 0.001f)) + { +// m_log.DebugFormat("[SCENE PRESENCE]: Discarded update from {0} to {1}, args {2} {3} {4} {5} {6} {7}", +// Name, remoteClient.Name, remoteClient.AgentId, UUID, remoteClient.SceneAgent.IsChildAgent, m_terseUpdateCount, Scene.RootTerseUpdatePeriod, Velocity.ApproxEquals(Vector3.Zero, 0.001f)); + + return; + } + } + + if (Scene.ChildTerseUpdatePeriod > 1 + && remoteClient.SceneAgent.IsChildAgent + && m_terseUpdateCount % Scene.ChildTerseUpdatePeriod != 0 + && !Velocity.ApproxEquals(Vector3.Zero, 0.001f)) + return; + //m_log.DebugFormat("[SCENE PRESENCE]: " + Name + " sending TerseUpdate to " + remoteClient.Name + " : Pos={0} Rot={1} Vel={2}", m_pos, Rotation, m_velocity); remoteClient.SendEntityUpdate( @@ -2417,7 +3329,11 @@ namespace OpenSim.Region.Framework.Scenes float distanceError = Vector3.Distance(OffsetPosition, expectedPosition); float speed = Velocity.Length(); - float velocidyDiff = Vector3.Distance(lastVelocitySentToAllClients, Velocity); + float velocityDiff = Vector3.Distance(lastVelocitySentToAllClients, Velocity); + +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Delta-v {0}, lastVelocity {1}, Velocity {2} for {3} in {4}", +// velocidyDiff, lastVelocitySentToAllClients, Velocity, Name, Scene.Name); // assuming 5 ms. worst case precision for timer, use 2x that // for distance error threshold @@ -2425,12 +3341,19 @@ namespace OpenSim.Region.Framework.Scenes if (speed < 0.01f // allow rotation updates if avatar position is unchanged || Math.Abs(distanceError) > distanceErrorThreshold - || velocidyDiff > 0.01f) // did velocity change from last update? + || velocityDiff > 0.01f) // did velocity change from last update? { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Update triggered with speed {0}, distanceError {1}, distanceThreshold {2}, delta-v {3} for {4} in {5}", +// speed, distanceError, distanceErrorThreshold, velocidyDiff, Name, Scene.Name); + lastVelocitySentToAllClients = Velocity; lastTerseUpdateToAllClientsTick = currentTick; lastPositionSentToAllClients = OffsetPosition; + m_terseUpdateCount++; + +// Console.WriteLine("Scheduled update for {0} in {1}", Name, Scene.Name); m_scene.ForEachClient(SendTerseUpdateToClient); } TriggerScenePresenceUpdated(); @@ -2456,24 +3379,30 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendCoarseLocationUpdate(avatarUUIDs, coarseLocations); } - public void SendInitialDataToMe() + public void SendInitialDataToClient() { + SentInitialDataToClient = true; + // Send all scene object to the new client - Util.FireAndForget(delegate + WorkManager.RunJob("SendInitialDataToClient", delegate { +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Sending initial data to {0} agent {1} in {2}, tp flags {3}", +// IsChildAgent ? "child" : "root", Name, Scene.Name, m_teleportFlags); + // we created a new ScenePresence (a new child agent) in a fresh region. // Request info about all the (root) agents in this region // Note: This won't send data *to* other clients in that region (children don't send) - SendOtherAgentsAvatarDataToMe(); - SendOtherAgentsAppearanceToMe(); + SendOtherAgentsAvatarDataToClient(); + SendOtherAgentsAppearanceToClient(); EntityBase[] entities = Scene.Entities.GetEntities(); - foreach(EntityBase e in entities) + foreach (EntityBase e in entities) { if (e != null && e is SceneObjectGroup) ((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient); } - }); + }, null, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name), false, true); } /// @@ -2506,26 +3435,33 @@ namespace OpenSim.Region.Framework.Scenes // getting other avatars information was initiated elsewhere immediately after the child circuit connected... don't do it // again here... this comes after the cached appearance check because the avatars // appearance goes into the avatar update packet - SendAvatarDataToAllAgents(); - SendAppearanceToAgent(this); + SendAvatarDataToAllClients(); + + // This invocation always shows up in the viewer logs as an error. Is it needed? + SendAppearanceToClient(this); // If we are using the the cached appearance then send it out to everyone if (cachedappearance) { - m_log.DebugFormat("[SCENE PRESENCE]: baked textures are in the cache for {0}", Name); + m_log.DebugFormat("[SCENE PRESENCE]: Baked textures are in the cache for {0} in {1}", Name, m_scene.Name); // If the avatars baked textures are all in the cache, then we have a // complete appearance... send it out, if not, then we'll send it when // the avatar finishes updating its appearance - SendAppearanceToAllOtherAgents(); + SendAppearanceToAllOtherClients(); } } + public void SendAvatarDataToAllClients() + { + SendAvatarDataToAllClients(true); + } + /// /// Send this agent's avatar data to all other root and child agents in the scene /// This agent must be root. This avatar will receive its own update. /// - public void SendAvatarDataToAllAgents() + public void SendAvatarDataToAllClients(bool full) { //m_log.DebugFormat("[SCENE PRESENCE] SendAvatarDataToAllAgents: {0} ({1})", Name, UUID); // only send update from root agents to other clients; children are only "listening posts" @@ -2538,12 +3474,17 @@ namespace OpenSim.Region.Framework.Scenes return; } + m_lastSize = Appearance.AvatarSize; + int count = 0; m_scene.ForEachScenePresence(delegate(ScenePresence scenePresence) - { - SendAvatarDataToAgent(scenePresence); - count++; - }); + { + if (full) + SendAvatarDataToClient(scenePresence); + else + scenePresence.ControllingClient.SendAvatarDataImmediate(this); + count++; + }); m_scene.StatsReporter.AddAgentUpdates(count); } @@ -2552,7 +3493,7 @@ namespace OpenSim.Region.Framework.Scenes /// Send avatar data for all other root agents to this agent, this agent /// can be either a child or root /// - public void SendOtherAgentsAvatarDataToMe() + public void SendOtherAgentsAvatarDataToClient() { int count = 0; m_scene.ForEachRootScenePresence(delegate(ScenePresence scenePresence) @@ -2561,7 +3502,7 @@ namespace OpenSim.Region.Framework.Scenes if (scenePresence.UUID == UUID) return; - scenePresence.SendAvatarDataToAgent(this); + scenePresence.SendAvatarDataToClient(this); count++; }); @@ -2572,9 +3513,9 @@ namespace OpenSim.Region.Framework.Scenes /// Send avatar data to an agent. /// /// - public void SendAvatarDataToAgent(ScenePresence avatar) + public void SendAvatarDataToClient(ScenePresence avatar) { - //m_log.DebugFormat("[SCENE PRESENCE] SendAvatarDataToAgent from {0} ({1}) to {2} ({3})", Name, UUID, avatar.Name, avatar.UUID); + //m_log.DebugFormat("[SCENE PRESENCE] SendAvatarDataToClient from {0} ({1}) to {2} ({3})", Name, UUID, avatar.Name, avatar.UUID); avatar.ControllingClient.SendAvatarDataImmediate(this); Animator.SendAnimPackToClient(avatar.ControllingClient); @@ -2584,9 +3525,9 @@ namespace OpenSim.Region.Framework.Scenes /// Send this agent's appearance to all other root and child agents in the scene /// This agent must be root. /// - public void SendAppearanceToAllOtherAgents() + public void SendAppearanceToAllOtherClients() { -// m_log.DebugFormat("[SCENE PRESENCE] SendAppearanceToAllOtherAgents: {0} {1}", Name, UUID); +// m_log.DebugFormat("[SCENE PRESENCE] SendAppearanceToAllOtherClients: {0} {1}", Name, UUID); // only send update from root agents to other clients; children are only "listening posts" if (IsChildAgent) @@ -2605,7 +3546,7 @@ namespace OpenSim.Region.Framework.Scenes if (scenePresence.UUID == UUID) return; - SendAppearanceToAgent(scenePresence); + SendAppearanceToClient(scenePresence); count++; }); @@ -2616,9 +3557,9 @@ namespace OpenSim.Region.Framework.Scenes /// Send appearance from all other root agents to this agent. this agent /// can be either root or child /// - public void SendOtherAgentsAppearanceToMe() + public void SendOtherAgentsAppearanceToClient() { -// m_log.DebugFormat("[SCENE PRESENCE] SendOtherAgentsAppearanceToMe: {0} {1}", Name, UUID); +// m_log.DebugFormat("[SCENE PRESENCE] SendOtherAgentsAppearanceToClient {0} {1}", Name, UUID); int count = 0; m_scene.ForEachRootScenePresence(delegate(ScenePresence scenePresence) @@ -2627,7 +3568,7 @@ namespace OpenSim.Region.Framework.Scenes if (scenePresence.UUID == UUID) return; - scenePresence.SendAppearanceToAgent(this); + scenePresence.SendAppearanceToClient(this); count++; }); @@ -2638,13 +3579,15 @@ namespace OpenSim.Region.Framework.Scenes /// Send appearance data to an agent. /// /// - public void SendAppearanceToAgent(ScenePresence avatar) + public void SendAppearanceToClient(ScenePresence avatar) { // m_log.DebugFormat( // "[SCENE PRESENCE]: Sending appearance data from {0} {1} to {2} {3}", Name, m_uuid, avatar.Name, avatar.UUID); avatar.ControllingClient.SendAppearance( UUID, Appearance.VisualParams, Appearance.Texture.GetBytes()); + + } #endregion @@ -2663,11 +3606,10 @@ namespace OpenSim.Region.Framework.Scenes } // Minimum Draw distance is 64 meters, the Radius of the draw distance sphere is 32m - if (Util.GetDistanceTo(AbsolutePosition, m_lastChildAgentUpdatePosition) >= Scene.ChildReprioritizationDistance || - Util.GetDistanceTo(CameraPosition, m_lastChildAgentUpdateCamPosition) >= Scene.ChildReprioritizationDistance) + if (Util.GetDistanceTo(AbsolutePosition, m_lastChildAgentUpdatePosition) >= Scene.ChildReprioritizationDistance) { m_lastChildAgentUpdatePosition = AbsolutePosition; - m_lastChildAgentUpdateCamPosition = CameraPosition; +// m_lastChildAgentUpdateCamPosition = CameraPosition; ChildAgentDataUpdate cadu = new ChildAgentDataUpdate(); cadu.ActiveGroupID = UUID.Zero.Guid; @@ -2694,10 +3636,11 @@ namespace OpenSim.Region.Framework.Scenes cadu.Velocity = Velocity; AgentPosition agentpos = new AgentPosition(); - agentpos.CopyFrom(cadu); + agentpos.CopyFrom(cadu, ControllingClient.SessionId); // Let's get this out of the update loop - Util.FireAndForget(delegate { m_scene.SendOutChildAgentUpdates(agentpos, this); }); + Util.FireAndForget( + o => m_scene.SendOutChildAgentUpdates(agentpos, this), null, "ScenePresence.SendOutChildAgentUpdates"); } } @@ -2719,140 +3662,84 @@ namespace OpenSim.Region.Framework.Scenes // If we don't have a PhysActor, we can't cross anyway // Also don't do this while sat, sitting avatars cross with the - // object they sit on. - if (ParentID != 0 || PhysicsActor == null) + // object they sit on. ParentUUID denoted a pending sit, don't + // interfere with it. + if (ParentID != 0 || PhysicsActor == null || ParentUUID != UUID.Zero) return; - if (!IsInTransit) - { - Vector3 pos2 = AbsolutePosition; - Vector3 vel = Velocity; - int neighbor = 0; - int[] fix = new int[2]; + if (IsInTransit) + return; - float timeStep = 0.1f; - pos2.X = pos2.X + (vel.X * timeStep); - pos2.Y = pos2.Y + (vel.Y * timeStep); - pos2.Z = pos2.Z + (vel.Z * timeStep); + Vector3 pos2 = AbsolutePosition; + Vector3 origPosition = pos2; + Vector3 vel = Velocity; - if (!IsInTransit) - { - // Checks if where it's headed exists a region - bool needsTransit = false; - if (m_scene.TestBorderCross(pos2, Cardinals.W)) - { - if (m_scene.TestBorderCross(pos2, Cardinals.S)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.SW, ref fix); - } - else if (m_scene.TestBorderCross(pos2, Cardinals.N)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.NW, ref fix); - } - else - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.W, ref fix); - } - } - else if (m_scene.TestBorderCross(pos2, Cardinals.E)) - { - if (m_scene.TestBorderCross(pos2, Cardinals.S)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.SE, ref fix); - } - else if (m_scene.TestBorderCross(pos2, Cardinals.N)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.NE, ref fix); - } - else - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.E, ref fix); - } - } - else if (m_scene.TestBorderCross(pos2, Cardinals.S)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.S, ref fix); - } - else if (m_scene.TestBorderCross(pos2, Cardinals.N)) - { - needsTransit = true; - neighbor = m_scene.HaveNeighbor(Cardinals.N, ref fix); - } + // Compute the future avatar position. + // If the avatar will be crossing, we force the crossing to happen now + // in the hope that this will make the avatar movement smoother when crossing. + pos2 += vel * 0.05f; - // Makes sure avatar does not end up outside region - if (neighbor <= 0) - { - if (needsTransit) - { - if (m_requestedSitTargetUUID == UUID.Zero) - { - bool isFlying = Flying; - RemoveFromPhysicalScene(); - - Vector3 pos = AbsolutePosition; - if (AbsolutePosition.X < 0) - pos.X += Velocity.X * 2; - else if (AbsolutePosition.X > Constants.RegionSize) - pos.X -= Velocity.X * 2; - if (AbsolutePosition.Y < 0) - pos.Y += Velocity.Y * 2; - else if (AbsolutePosition.Y > Constants.RegionSize) - pos.Y -= Velocity.Y * 2; - Velocity = Vector3.Zero; - AbsolutePosition = pos; - -// m_log.DebugFormat("[SCENE PRESENCE]: Prevented flyoff for {0} at {1}", Name, AbsolutePosition); - - AddToPhysicalScene(isFlying); - } - } - } - else if (neighbor > 0) - { - if (!CrossToNewRegion()) - { - if (m_requestedSitTargetUUID == UUID.Zero) - { - bool isFlying = Flying; - RemoveFromPhysicalScene(); - - Vector3 pos = AbsolutePosition; - if (AbsolutePosition.X < 0) - pos.X += Velocity.X * 2; - else if (AbsolutePosition.X > Constants.RegionSize) - pos.X -= Velocity.X * 2; - if (AbsolutePosition.Y < 0) - pos.Y += Velocity.Y * 2; - else if (AbsolutePosition.Y > Constants.RegionSize) - pos.Y -= Velocity.Y * 2; - Velocity = Vector3.Zero; - AbsolutePosition = pos; - - AddToPhysicalScene(isFlying); - } - } - } - } - else + if (m_scene.PositionIsInCurrentRegion(pos2)) + return; + + m_log.DebugFormat("{0} CheckForBorderCrossing: position outside region. {1} in {2} at pos {3}", + LogHeader, Name, Scene.Name, pos2); + + // Disconnect from the current region + bool isFlying = Flying; + RemoveFromPhysicalScene(); + + // pos2 is the forcasted position so make that the 'current' position so the crossing + // code will move us into the newly addressed region. + m_pos = pos2; + + if (CrossToNewRegion()) + { + AddToPhysicalScene(isFlying); + } + else + { + // Tried to make crossing happen but it failed. + if (m_requestedSitTargetUUID == UUID.Zero) { - // This constant has been inferred from experimentation - // I'm not sure what this value should be, so I tried a few values. - timeStep = 0.04f; - pos2 = AbsolutePosition; - pos2.X = pos2.X + (vel.X * timeStep); - pos2.Y = pos2.Y + (vel.Y * timeStep); - // Don't touch the Z - m_pos = pos2; - m_log.DebugFormat("[SCENE PRESENCE]: In transit m_pos={0}", m_pos); + m_log.DebugFormat("{0} CheckForBorderCrossing: Crossing failed. Restoring old position.", LogHeader); + + Velocity = Vector3.Zero; + AbsolutePosition = EnforceSanityOnPosition(origPosition); + + AddToPhysicalScene(isFlying); } - } + } + } + + // Given a position, make sure it is within the current region. + // If just outside some border, the returned position will be just inside the border on that side. + private Vector3 EnforceSanityOnPosition(Vector3 origPosition) + { + const float borderFudge = 0.1f; + Vector3 ret = origPosition; + + // Sanity checking on the position to make sure it is in the region we couldn't cross from + float extentX = (float)m_scene.RegionInfo.RegionSizeX; + float extentY = (float)m_scene.RegionInfo.RegionSizeY; + IRegionCombinerModule combiner = m_scene.RequestModuleInterface(); + if (combiner != null) + { + // If a mega-region, the size could be much bigger + Vector2 megaExtent = combiner.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID); + extentX = megaExtent.X; + extentY = megaExtent.Y; + } + if (ret.X < 0) + ret.X = borderFudge; + else if (ret.X >= extentX) + ret.X = extentX - borderFudge; + if (ret.Y < 0) + ret.Y = borderFudge; + else if (ret.Y >= extentY) + ret.Y = extentY - borderFudge; + + return ret; } /// @@ -2873,18 +3760,13 @@ namespace OpenSim.Region.Framework.Scenes } } - public void RestoreInCurrentScene() - { - AddToPhysicalScene(false); // not exactly false - } - public void Reset() { // m_log.DebugFormat("[SCENE PRESENCE]: Resetting {0} in {1}", Name, Scene.RegionInfo.RegionName); // Put the child agent back at the center AbsolutePosition - = new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f), 70); + = new Vector3(((float)m_scene.RegionInfo.RegionSizeX * 0.5f), ((float)m_scene.RegionInfo.RegionSizeY * 0.5f), 70); Animator.ResetAnimations(); } @@ -2911,13 +3793,13 @@ namespace OpenSim.Region.Framework.Scenes if (handle != Scene.RegionInfo.RegionHandle) { uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; + Util.RegionHandleToRegionLoc(handle, out x, out y); // m_log.Debug("---> x: " + x + "; newx:" + newRegionX + "; Abs:" + (int)Math.Abs((int)(x - newRegionX))); // m_log.Debug("---> y: " + y + "; newy:" + newRegionY + "; Abs:" + (int)Math.Abs((int)(y - newRegionY))); - if (Util.IsOutsideView(DrawDistance, x, newRegionX, y, newRegionY)) + float dist = (float)Math.Max(Scene.DefaultDrawDistance, + (float)Math.Max(Scene.RegionInfo.RegionSizeX, Scene.RegionInfo.RegionSizeY)); + if (Util.IsOutsideView(dist, x, newRegionX, y, newRegionY)) { byebyeRegions.Add(handle); } @@ -2927,10 +3809,12 @@ namespace OpenSim.Region.Framework.Scenes if (byebyeRegions.Count > 0) { m_log.Debug("[SCENE PRESENCE]: Closing " + byebyeRegions.Count + " child agents"); - Util.FireAndForget(delegate - { - m_scene.SceneGridService.SendCloseChildAgentConnections(ControllingClient.AgentId, byebyeRegions); - }); + + AgentCircuitData acd = Scene.AuthenticateHandler.GetAgentCircuitData(UUID); + string auth = string.Empty; + if (acd != null) + auth = acd.SessionID.ToString(); + m_scene.SceneGridService.SendCloseChildAgentConnections(ControllingClient.AgentId, auth, byebyeRegions); } foreach (ulong handle in byebyeRegions) @@ -2971,36 +3855,45 @@ namespace OpenSim.Region.Framework.Scenes #region Child Agent Updates - public void ChildAgentDataUpdate(AgentData cAgentData) + public void UpdateChildAgent(AgentData cAgentData) { // m_log.Debug(" >>> ChildAgentDataUpdate <<< " + Scene.RegionInfo.RegionName); if (!IsChildAgent) return; CopyFrom(cAgentData); + + m_updateAgentReceivedAfterTransferEvent.Set(); } private static Vector3 marker = new Vector3(-1f, -1f, -1f); + /// /// This updates important decision making data about a child agent /// The main purpose is to figure out what objects to send to a child agent that's in a neighboring region /// - public void ChildAgentDataUpdate(AgentPosition cAgentData, uint tRegionX, uint tRegionY, uint rRegionX, uint rRegionY) + public void UpdateChildAgent(AgentPosition cAgentData, uint tRegionX, uint tRegionY, uint rRegionX, uint rRegionY) { if (!IsChildAgent) return; - //m_log.Debug(" >>> ChildAgentPositionUpdate <<< " + rRegionX + "-" + rRegionY); - int shiftx = ((int)rRegionX - (int)tRegionX) * (int)Constants.RegionSize; - int shifty = ((int)rRegionY - (int)tRegionY) * (int)Constants.RegionSize; +// m_log.DebugFormat( +// "[SCENE PRESENCE]: ChildAgentPositionUpdate for {0} in {1}, tRegion {2},{3}, rRegion {4},{5}, pos {6}", +// Name, Scene.Name, tRegionX, tRegionY, rRegionX, rRegionY, cAgentData.Position); + + // Find the distance (in meters) between the two regions + // XXX: We cannot use Util.RegionLocToHandle() here because a negative value will silently overflow the + // uint + int shiftx = (int)(((int)rRegionX - (int)tRegionX) * Constants.RegionSize); + int shifty = (int)(((int)rRegionY - (int)tRegionY) * Constants.RegionSize); Vector3 offset = new Vector3(shiftx, shifty, 0f); // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = cAgentData.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = cAgentData.Far; + // DrawDistance = Scene.DefaultDrawDistance; if (cAgentData.Position != marker) // UGH!! m_pos = cAgentData.Position + offset; @@ -3027,6 +3920,7 @@ namespace OpenSim.Region.Framework.Scenes cAgent.AgentID = UUID; cAgent.RegionID = Scene.RegionInfo.RegionID; + cAgent.SessionID = ControllingClient.SessionId; cAgent.Position = AbsolutePosition; cAgent.Velocity = m_velocity; @@ -3061,6 +3955,9 @@ namespace OpenSim.Region.Framework.Scenes cAgent.AlwaysRun = SetAlwaysRun; cAgent.Appearance = new AvatarAppearance(Appearance); + + cAgent.ParentPart = ParentUUID; + cAgent.SitOffset = PrevSitOffset; lock (scriptedcontrols) { @@ -3069,7 +3966,7 @@ namespace OpenSim.Region.Framework.Scenes foreach (ScriptControllers c in scriptedcontrols.Values) { - controls[i++] = new ControllerData(c.itemID, (uint)c.ignoreControls, (uint)c.eventControls); + controls[i++] = new ControllerData(c.objectID, c.itemID, (uint)c.ignoreControls, (uint)c.eventControls); } cAgent.Controllers = controls; } @@ -3089,8 +3986,6 @@ namespace OpenSim.Region.Framework.Scenes private void CopyFrom(AgentData cAgent) { - m_originRegionID = cAgent.RegionID; - m_callbackURI = cAgent.CallbackURI; // m_log.DebugFormat( // "[SCENE PRESENCE]: Set callback for {0} in {1} to {2} in CopyFrom()", @@ -3102,12 +3997,14 @@ namespace OpenSim.Region.Framework.Scenes CameraAtAxis = cAgent.AtAxis; CameraLeftAxis = cAgent.LeftAxis; CameraUpAxis = cAgent.UpAxis; + ParentUUID = cAgent.ParentPart; + PrevSitOffset = cAgent.SitOffset; // When we get to the point of re-computing neighbors everytime this // changes, then start using the agent's drawdistance rather than the // region's draw distance. - // DrawDistance = cAgent.Far; - DrawDistance = Scene.DefaultDrawDistance; + DrawDistance = cAgent.Far; + // DrawDistance = Scene.DefaultDrawDistance; if ((cAgent.Throttles != null) && cAgent.Throttles.Length > 0) ControllingClient.SetChildAgentThrottle(cAgent.Throttles); @@ -3139,6 +4036,7 @@ namespace OpenSim.Region.Framework.Scenes foreach (ControllerData c in cAgent.Controllers) { ScriptControllers sc = new ScriptControllers(); + sc.objectID = c.ObjectID; sc.itemID = c.ItemID; sc.ignoreControls = (ScriptControlled)c.IgnoreControls; sc.eventControls = (ScriptControlled)c.EventControls; @@ -3159,7 +4057,27 @@ namespace OpenSim.Region.Framework.Scenes Animator.Animations.SetImplicitDefaultAnimation(cAgent.AnimState.AnimID, cAgent.AnimState.SequenceNum, UUID.Zero); if (Scene.AttachmentsModule != null) - Scene.AttachmentsModule.CopyAttachments(cAgent, this); + { + // If the JobEngine is running we can schedule this job now and continue rather than waiting for all + // attachments to copy, which might take a long time in the Hypergrid case as the entire inventory + // graph is inspected for each attachments and assets possibly fetched. + // + // We don't need to worry about a race condition as the job to later start the scripts is also + // JobEngine scheduled and so will always occur after this task. + // XXX: This will not be true if JobEngine ever gets more than one thread. + WorkManager.RunJob( + "CopyAttachments", + o => Scene.AttachmentsModule.CopyAttachments(cAgent, this), + null, + string.Format("Copy attachments for {0} entering {1}", Name, Scene.Name), + true); + } + + // This must occur after attachments are copied or scheduled to be copied, as it releases the CompleteMovement() calling thread + // originating from the client completing a teleport. Otherwise, CompleteMovement() code to restart + // script attachments can outrace this thread. + lock (m_originRegionIDAccessLock) + m_originRegionID = cAgent.RegionID; } public bool CopyAgent(out IAgentData agent) @@ -3180,8 +4098,6 @@ namespace OpenSim.Region.Framework.Scenes { Vector3 force = m_forceToApply.Value; - Updated = true; - Velocity = force; m_forceToApply = null; @@ -3206,20 +4122,23 @@ namespace OpenSim.Region.Framework.Scenes } if (Appearance.AvatarHeight == 0) - Appearance.SetHeight(); - - PhysicsScene scene = m_scene.PhysicsScene; - - Vector3 pVec = AbsolutePosition; - +// Appearance.SetHeight(); + Appearance.SetSize(new Vector3(0.45f,0.6f,1.9f)); + +/* PhysicsActor = scene.AddAvatar( LocalId, Firstname + "." + Lastname, pVec, - new Vector3(0f, 0f, Appearance.AvatarHeight), isFlying); + new Vector3(0.45f, 0.6f, Appearance.AvatarHeight), isFlying); +*/ + + PhysicsActor = m_scene.PhysicsScene.AddAvatar( + LocalId, Firstname + "." + Lastname, AbsolutePosition, Velocity, + Appearance.AvatarBoxSize, isFlying); //PhysicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients; PhysicsActor.OnCollisionUpdate += PhysicsCollisionUpdate; PhysicsActor.OnOutOfBounds += OutOfBoundsCall; // Called for PhysicsActors when there's something wrong - PhysicsActor.SubscribeEvents(500); + PhysicsActor.SubscribeEvents(100); PhysicsActor.LocalID = LocalId; } @@ -3233,6 +4152,7 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendAgentAlertMessage("Physics is having a problem with your avatar. You may not be able to move until you relog.", true); } + /// /// Event called by the physics plugin to tell the avatar about a collision. /// @@ -3246,7 +4166,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void PhysicsCollisionUpdate(EventArgs e) { - if (IsChildAgent) + if (IsChildAgent || Animator == null) return; //if ((Math.Abs(Velocity.X) > 0.1e-9f) || (Math.Abs(Velocity.Y) > 0.1e-9f)) @@ -3255,14 +4175,14 @@ namespace OpenSim.Region.Framework.Scenes // if (m_updateCount > 0) // { - Animator.UpdateMovementAnimations(); + if (Animator.UpdateMovementAnimations()) + TriggerScenePresenceUpdated(); // m_updateCount--; // } CollisionEventUpdate collisionData = (CollisionEventUpdate)e; Dictionary coldata = collisionData.m_objCollisionList; - CollisionPlane = Vector4.UnitW; // // No collisions at all means we may be flying. Update always // // to make falling work @@ -3272,34 +4192,7 @@ namespace OpenSim.Region.Framework.Scenes // m_lastColCount = coldata.Count; // } - if (coldata.Count != 0) - { - switch (Animator.CurrentMovementAnimation) - { - case "STAND": - case "WALK": - case "RUN": - case "CROUCH": - case "CROUCHWALK": - { - ContactPoint lowest; - lowest.SurfaceNormal = Vector3.Zero; - lowest.Position = Vector3.Zero; - lowest.Position.Z = Single.NaN; - - foreach (ContactPoint contact in coldata.Values) - { - if (Single.IsNaN(lowest.Position.Z) || contact.Position.Z < lowest.Position.Z) - { - lowest = contact; - } - } - - CollisionPlane = new Vector4(-lowest.SurfaceNormal, -Vector3.Dot(lowest.Position, lowest.SurfaceNormal)); - } - break; - } - } + CollisionPlane = Vector4.UnitW; // Gods do not take damage and Invulnerable is set depending on parcel/region flags if (Invulnerable || GodLevel > 0) @@ -3398,6 +4291,14 @@ namespace OpenSim.Region.Framework.Scenes // m_reprioritizationTimer.Dispose(); RemoveFromPhysicalScene(); + + m_scene.EventManager.OnRegionHeartbeatEnd -= RegionHeartbeatEnd; + +// if (Animator != null) +// Animator.Close(); + Animator = null; + + LifecycleState = ScenePresenceState.Removed; } public void AddAttachment(SceneObjectGroup gobj) @@ -3602,7 +4503,7 @@ namespace OpenSim.Region.Framework.Scenes } } } - }); + }, null, "ScenePresence.SendScriptEventToAttachments"); } /// @@ -3631,10 +4532,18 @@ namespace OpenSim.Region.Framework.Scenes public void RegisterControlEventsToScript(int controls, int accept, int pass_on, uint Obj_localID, UUID Script_item_UUID) { + SceneObjectPart p = m_scene.GetSceneObjectPart(Obj_localID); + if (p == null) + return; + + ControllingClient.SendTakeControls(controls, false, false); + ControllingClient.SendTakeControls(controls, true, false); + ScriptControllers obj = new ScriptControllers(); obj.ignoreControls = ScriptControlled.CONTROL_ZERO; obj.eventControls = ScriptControlled.CONTROL_ZERO; + obj.objectID = p.ParentGroup.UUID; obj.itemID = Script_item_UUID; if (pass_on == 0 && accept == 0) { @@ -3683,6 +4592,21 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendTakeControls(int.MaxValue, false, false); } + private void UnRegisterSeatControls(UUID obj) + { + List takers = new List(); + + foreach (ScriptControllers c in scriptedcontrols.Values) + { + if (c.objectID == obj) + takers.Add(c.itemID); + } + foreach (UUID t in takers) + { + UnRegisterControlEventsToScript(0, t); + } + } + public void UnRegisterControlEventsToScript(uint Obj_localID, UUID Script_item_UUID) { ScriptControllers takecontrols; @@ -3899,6 +4823,7 @@ namespace OpenSim.Region.Framework.Scenes (m_teleportFlags & TeleportFlags.ViaLocation) != 0 || (m_teleportFlags & Constants.TeleportFlags.ViaHGLogin) != 0) { + if (GodLevel < 200 && ((!m_scene.Permissions.IsGod(m_uuid) && !m_scene.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_uuid)) || @@ -3907,7 +4832,14 @@ namespace OpenSim.Region.Framework.Scenes { SpawnPoint[] spawnPoints = m_scene.RegionInfo.RegionSettings.SpawnPoints().ToArray(); if (spawnPoints.Length == 0) + { + if(m_scene.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_uuid)) + { + pos.X = 128.0f; + pos.Y = 128.0f; + } return; + } int index; bool selected = false; @@ -3916,6 +4848,8 @@ namespace OpenSim.Region.Framework.Scenes { case "random": + if (spawnPoints.Length == 0) + return; do { index = Util.RandomClass.Next(spawnPoints.Length - 1); @@ -3927,6 +4861,7 @@ namespace OpenSim.Region.Framework.Scenes // SpawnPoint sp = spawnPoints[index]; ILandObject land = m_scene.LandChannel.GetLandObject(spawnPosition.X, spawnPosition.Y); + if (land == null || land.IsEitherBannedOrRestricted(UUID)) selected = false; else @@ -3999,8 +4934,15 @@ namespace OpenSim.Region.Framework.Scenes } } + // Modify landing point based on possible banning, telehubs or parcel restrictions. private void CheckAndAdjustLandingPoint(ref Vector3 pos) { + string reason; + + // Honor bans + if (!m_scene.TestLandRestrictions(UUID, out reason, ref pos.X, ref pos.Y)) + return; + SceneObjectGroup telehub = null; if (m_scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero && (telehub = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject)) != null) { @@ -4040,11 +4982,119 @@ namespace OpenSim.Region.Framework.Scenes pos = land.LandData.UserLocation; } } - + land.SendLandUpdateToClient(ControllingClient); } } + private DetectedObject CreateDetObject(SceneObjectPart obj) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = obj.UUID; + detobj.nameStr = obj.Name; + detobj.ownerUUID = obj.OwnerID; + detobj.posVector = obj.AbsolutePosition; + detobj.rotQuat = obj.GetWorldRotation(); + detobj.velVector = obj.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = obj.GroupID; + + return detobj; + } + + private DetectedObject CreateDetObject(ScenePresence av) + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = av.UUID; + detobj.nameStr = av.ControllingClient.Name; + detobj.ownerUUID = av.UUID; + detobj.posVector = av.AbsolutePosition; + detobj.rotQuat = av.Rotation; + detobj.velVector = av.Velocity; + detobj.colliderType = 0; + detobj.groupUUID = av.ControllingClient.ActiveGroupId; + + return detobj; + } + + private DetectedObject CreateDetObjectForGround() + { + DetectedObject detobj = new DetectedObject(); + detobj.keyUUID = UUID.Zero; + detobj.nameStr = ""; + detobj.ownerUUID = UUID.Zero; + detobj.posVector = AbsolutePosition; + detobj.rotQuat = Quaternion.Identity; + detobj.velVector = Vector3.Zero; + detobj.colliderType = 0; + detobj.groupUUID = UUID.Zero; + + return detobj; + } + + private ColliderArgs CreateColliderArgs(SceneObjectPart dest, List colliders) + { + ColliderArgs colliderArgs = new ColliderArgs(); + List colliding = new List(); + foreach (uint localId in colliders) + { + if (localId == 0) + continue; + + SceneObjectPart obj = m_scene.GetSceneObjectPart(localId); + if (obj != null) + { + if (!dest.CollisionFilteredOut(obj.UUID, obj.Name)) + colliding.Add(CreateDetObject(obj)); + } + else + { + ScenePresence av = m_scene.GetScenePresence(localId); + if (av != null && (!av.IsChildAgent)) + { + if (!dest.CollisionFilteredOut(av.UUID, av.Name)) + colliding.Add(CreateDetObject(av)); + } + } + } + + colliderArgs.Colliders = colliding; + + return colliderArgs; + } + + private delegate void ScriptCollidingNotification(uint localID, ColliderArgs message); + + private void SendCollisionEvent(SceneObjectGroup dest, scriptEvents ev, List colliders, ScriptCollidingNotification notify) + { + ColliderArgs CollidingMessage; + + if (colliders.Count > 0) + { + if ((dest.RootPart.ScriptEvents & ev) != 0) + { + CollidingMessage = CreateColliderArgs(dest.RootPart, colliders); + + if (CollidingMessage.Colliders.Count > 0) + notify(dest.RootPart.LocalId, CollidingMessage); + } + } + } + + private void SendLandCollisionEvent(SceneObjectGroup dest, scriptEvents ev, ScriptCollidingNotification notify) + { + if ((dest.RootPart.ScriptEvents & ev) != 0) + { + ColliderArgs LandCollidingMessage = new ColliderArgs(); + List colliding = new List(); + + colliding.Add(CreateDetObjectForGround()); + LandCollidingMessage.Colliders = colliding; + + notify(dest.RootPart.LocalId, LandCollidingMessage); + } + } + private void TeleportFlagsDebug() { // Some temporary debugging help to show all the TeleportFlags we have... @@ -4069,6 +5119,5 @@ namespace OpenSim.Region.Framework.Scenes m_log.InfoFormat("[SCENE PRESENCE]: TELEPORT ******************"); } - } } -- cgit v1.1