public void HandleAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData)
{
//if (m_isChildAgent)
//{
// // m_log.Debug("DEBUG: HandleAgentUpdate: child agent");
// return;
//}
m_perfMonMS = Util.EnvironmentTickCount();
++m_movementUpdateCount;
if (m_movementUpdateCount < 1)
m_movementUpdateCount = 1;
#region Sanity Checking
// This is irritating. Really.
if (!AbsolutePosition.IsFinite())
{
RemoveFromPhysicalScene();
m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error #9999902");
m_pos = m_LastFinitePos;
if (!m_pos.IsFinite())
{
m_pos.X = 127f;
m_pos.Y = 127f;
m_pos.Z = 127f;
m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error #9999903");
}
AddToPhysicalScene(false);
}
else
{
m_LastFinitePos = m_pos;
}
#endregion Sanity Checking
#region Inputs
AgentManager.ControlFlags flags = (AgentManager.ControlFlags)agentData.ControlFlags;
Quaternion bodyRotation = agentData.BodyRotation;
// Camera location in world. We'll need to raytrace
// from this location from time to time.
m_CameraCenter = agentData.CameraCenter;
if (Vector3.Distance(m_lastCameraCenter, m_CameraCenter) >= Scene.RootReprioritizationDistance)
{
ReprioritizeUpdates();
m_lastCameraCenter = m_CameraCenter;
}
// Use these three vectors to figure out what the agent is looking at
// Convert it to a Matrix and/or Quaternion
m_CameraAtAxis = agentData.CameraAtAxis;
m_CameraLeftAxis = agentData.CameraLeftAxis;
m_CameraUpAxis = agentData.CameraUpAxis;
// The Agent's Draw distance setting
m_DrawDistance = agentData.Far;
// Check if Client has camera in 'follow cam' or 'build' mode.
Vector3 camdif = (Vector3.One * m_bodyRot - Vector3.One * CameraRotation);
m_followCamAuto = ((m_CameraUpAxis.Z > 0.959f && m_CameraUpAxis.Z < 0.98f)
&& (Math.Abs(camdif.X) < 0.4f && Math.Abs(camdif.Y) < 0.4f)) ? true : false;
m_mouseLook = (flags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0;
m_leftButtonDown = (flags & AgentManager.ControlFlags.AGENT_CONTROL_LBUTTON_DOWN) != 0;
#endregion Inputs
if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) != 0)
{
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())
{
if (m_followCamAuto)
{
Vector3 posAdjusted = m_pos + HEAD_ADJUSTMENT;
m_scene.PhysicsScene.RaycastWorld(m_pos, Vector3.Normalize(m_CameraCenter - posAdjusted), Vector3.Distance(m_CameraCenter, posAdjusted) + 0.3f, RayCastCameraCallback);
}
}
lock (scriptedcontrols)
{
if (scriptedcontrols.Count > 0)
{
SendControlToScripts((uint)flags);
flags = RemoveIgnoredControls(flags, IgnoredControls);
}
}
if (m_autopilotMoving)
CheckAtSitTarget();
if ((flags & AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND) != 0)
{
// TODO: This doesn't prevent the user from walking yet.
// Setting parent ID would fix this, if we knew what value
// to use. Or we could add a m_isSitting variable.
//Animator.TrySetMovementAnimation("SIT_GROUND_CONSTRAINED");
SitGround = true;
}
// In the future, these values might need to go global.
// Here's where you get them.
m_AgentControlFlags = flags;
m_headrotation = agentData.HeadRotation;
m_state = agentData.State;
PhysicsActor actor = PhysicsActor;
if (actor == null)
{
return;
}
bool update_movementflag = false;
if (m_allowMovement && !SitGround)
{
if (agentData.UseClientAgentPosition)
{
m_moveToPositionInProgress = (agentData.ClientAgentPosition - AbsolutePosition).Length() > 0.2f;
m_moveToPositionTarget = agentData.ClientAgentPosition;
}
int i = 0;
bool update_rotation = false;
bool DCFlagKeyPressed = false;
Vector3 agent_control_v3 = Vector3.Zero;
Quaternion q = bodyRotation;
bool oldflying = PhysicsActor.Flying;
if (m_forceFly)
actor.Flying = true;
else if (m_flyDisabled)
actor.Flying = false;
else
actor.Flying = ((flags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0);
if (actor.Flying != oldflying)
update_movementflag = true;
if (q != m_bodyRot)
{
m_bodyRot = q;
update_rotation = true;
}
if (m_parentID == 0)
{
bool bAllowUpdateMoveToPosition = false;
bool bResetMoveToPosition = 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 ((m_mouseLook && !m_physicsActor.Flying) || (m_leftButtonDown && !m_physicsActor.Flying))
dirVectors = GetWalkDirectionVectors();
else
dirVectors = Dir_Vectors;
// The fact that m_movementflag is a byte needs to be fixed
// it really should be a uint
uint nudgehack = 250;
foreach (Dir_ControlFlags DCF in DIR_CONTROL_FLAGS)
{
if (((uint)flags & (uint)DCF) != 0)
{
bResetMoveToPosition = true;
DCFlagKeyPressed = true;
try
{
agent_control_v3 += dirVectors[i];
//m_log.DebugFormat("[Motion]: {0}, {1}",i, dirVectors[i]);
}
catch (IndexOutOfRangeException)
{
// Why did I get this?
}
if ((m_movementflag & (byte)(uint)DCF) == 0)
{
if (DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_BACKWARD_NUDGE)
{
m_movementflag |= (byte)nudgehack;
}
m_movementflag += (byte)(uint)DCF;
update_movementflag = true;
}
}
else
{
if ((m_movementflag & (byte)(uint)DCF) != 0 ||
((DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_BACKWARD_NUDGE)
&& ((m_movementflag & (byte)nudgehack) == nudgehack))
) // This or is for Nudge forward
{
m_movementflag -= ((byte)(uint)DCF);
update_movementflag = true;
/*
if ((DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD_NUDGE || DCF == Dir_ControlFlags.DIR_CONTROL_FLAG_BACKWARD_NUDGE)
&& ((m_movementflag & (byte)nudgehack) == nudgehack))
{
m_log.Debug("Removed Hack flag");
}
*/
}
else
{
bAllowUpdateMoveToPosition = true;
}
}
i++;
}
//Paupaw:Do Proper PID for Autopilot here
if (bResetMoveToPosition)
{
m_moveToPositionTarget = Vector3.Zero;
m_moveToPositionInProgress = false;
update_movementflag = true;
bAllowUpdateMoveToPosition = false;
}
if (bAllowUpdateMoveToPosition && (m_moveToPositionInProgress && !m_autopilotMoving))
{
//Check the error term of the current position in relation to the target position
if (Util.GetDistanceTo(AbsolutePosition, m_moveToPositionTarget) <= 0.5f)
{
// we are close enough to the target
m_moveToPositionTarget = Vector3.Zero;
m_moveToPositionInProgress = false;
update_movementflag = true;
}
else
{
try
{
// move avatar in 2D at one meter/second towards target, in avatar coordinate frame.
// This movement vector gets added to the velocity through AddNewMovement().
// Theoretically we might need a more complex PID approach here if other
// unknown forces are acting on the avatar and we need to adaptively respond
// to such forces, but the following simple approach seems to works fine.
Vector3 LocalVectorToTarget3D =
(m_moveToPositionTarget - AbsolutePosition) // vector from cur. pos to target in global coords
* Matrix4.CreateFromQuaternion(Quaternion.Inverse(bodyRotation)); // change to avatar coords
// Ignore z component of vector
Vector3 LocalVectorToTarget2D = new Vector3((float)(LocalVectorToTarget3D.X), (float)(LocalVectorToTarget3D.Y), 0f);
LocalVectorToTarget2D.Normalize();
agent_control_v3 += LocalVectorToTarget2D;
// update avatar movement flags. the avatar coordinate system is as follows:
//
// +X (forward)
//
// ^
// |
// |
// |
// |
// (left) +Y <--------o--------> -Y
// avatar
// |
// |
// |
// |
// v
// -X
//
// based on the above avatar coordinate system, classify the movement into
// one of left/right/back/forward.
if (LocalVectorToTarget2D.Y > 0)//MoveLeft
{
m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_LEFT;
//AgentControlFlags
AgentControlFlags |= (uint)Dir_ControlFlags.DIR_CONTROL_FLAG_LEFT;
update_movementflag = true;
}
else if (LocalVectorToTarget2D.Y < 0) //MoveRight
{
m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_RIGHT;
AgentControlFlags |= (uint)Dir_ControlFlags.DIR_CONTROL_FLAG_RIGHT;
update_movementflag = true;
}
if (LocalVectorToTarget2D.X < 0) //MoveBack
{
m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_BACK;
AgentControlFlags |= (uint)Dir_ControlFlags.DIR_CONTROL_FLAG_BACK;
update_movementflag = true;
}
else if (LocalVectorToTarget2D.X > 0) //Move Forward
{
m_movementflag += (byte)(uint)Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD;
AgentControlFlags |= (uint)Dir_ControlFlags.DIR_CONTROL_FLAG_FORWARD;
update_movementflag = true;
}
}
catch (Exception e)
{
//Avoid system crash, can be slower but...
m_log.DebugFormat("Crash! {0}", e.ToString());
}
}
}
}
// Cause the avatar to stop flying if it's colliding
// with something with the down arrow pressed.
// Only do this if we're flying
if (m_physicsActor != null && m_physicsActor.Flying && !m_forceFly)
{
// 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 collision requirements fulfilled?
bool colliding = (m_physicsActor.IsColliding == true);
if (m_physicsActor.Flying && colliding && 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();
}
}
// 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))
{
// m_log.DebugFormat("{0} {1}", update_movementflag, (update_rotation && DCFlagKeyPressed));
// m_log.DebugFormat(
// "In {0} adding velocity to {1} of {2}", m_scene.RegionInfo.RegionName, Name, agent_control_v3);
AddNewMovement(agent_control_v3, q);
}
}
if (update_movementflag && ((flags & AgentManager.ControlFlags.AGENT_CONTROL_SIT_ON_GROUND) == 0) && (m_parentID == 0) && !SitGround)
Animator.UpdateMovementAnimations();
m_scene.EventManager.TriggerOnClientMovement(this);
m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS));
}