1
0

Merge pull request #2446 from cuberite/asyncpathfinder

Fixed a position bug in the pathfinder
This commit is contained in:
Tiger Wang 2015-08-26 23:13:13 +01:00
commit 3b8dc45dc3
4 changed files with 25 additions and 101 deletions

View File

@ -76,8 +76,8 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_Target(nullptr) , m_Target(nullptr)
, m_Path(nullptr) , m_Path(nullptr)
, m_IsFollowingPath(false) , m_IsFollowingPath(false)
, m_PathfinderActivated(false)
, m_GiveUpCounter(0) , m_GiveUpCounter(0)
, m_TicksSinceLastPathReset(1000)
, m_LastGroundHeight(POSY_TOINT) , m_LastGroundHeight(POSY_TOINT)
, m_JumpCoolDown(0) , m_JumpCoolDown(0)
, m_IdleInterval(0) , m_IdleInterval(0)
@ -124,15 +124,10 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
bool cMonster::TickPathFinding(cChunk & a_Chunk) bool cMonster::TickPathFinding(cChunk & a_Chunk)
{ {
if (!m_IsFollowingPath) if (!m_PathfinderActivated)
{ {
return false; return false;
} }
if (m_TicksSinceLastPathReset < 1000)
{
// No need to count beyond 1000. 1000 is arbitary here.
++m_TicksSinceLastPathReset;
}
if (ReachedFinalDestination()) if (ReachedFinalDestination())
{ {
@ -140,26 +135,6 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
return false; return false;
} }
if ((m_FinalDestination - m_PathFinderDestination).Length() > 0.25) // if the distance between where we're going and where we should go is too big.
{
/* If we reached the last path waypoint,
Or if we haven't re-calculated for too long.
Interval is proportional to distance squared, and its minimum is 10.
(Recalculate lots when close, calculate rarely when far) */
if (
((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
)
{
/* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
This is a workaround till we get better path recalculation. */
if (!m_NoPathToTarget)
{
ResetPathFinding();
}
}
}
if (m_Path == nullptr) if (m_Path == nullptr)
{ {
if (!EnsureProperDestination(a_Chunk)) if (!EnsureProperDestination(a_Chunk))
@ -167,21 +142,17 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement. StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
return false; return false;
} }
m_NoPathToTarget = false; m_GiveUpCounter = 40;
m_NoMoreWayPoints = false; m_Path = new cPath(a_Chunk, GetPosition(), m_FinalDestination, 20, GetWidth(), GetHeight());
m_PathFinderDestination = m_FinalDestination;
m_Path = new cPath(a_Chunk, GetPosition(), m_PathFinderDestination, 20, GetWidth(), GetHeight());
} }
switch (m_Path->Step(a_Chunk)) switch (m_Path->Step(a_Chunk))
{ {
case ePathFinderStatus::NEARBY_FOUND: case ePathFinderStatus::NEARBY_FOUND:
{ {
m_NoPathToTarget = true; m_FinalDestination = m_Path->AcceptNearbyPath();
m_PathFinderDestination = m_Path->AcceptNearbyPath();
break; break;
} }
case ePathFinderStatus::PATH_NOT_FOUND: case ePathFinderStatus::PATH_NOT_FOUND:
{ {
StopMovingToPosition(); // Try to calculate a path again. StopMovingToPosition(); // Try to calculate a path again.
@ -195,9 +166,10 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
} }
case ePathFinderStatus::PATH_FOUND: case ePathFinderStatus::PATH_FOUND:
{ {
if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0)) if ((--m_GiveUpCounter) == 0)
{ {
if (m_EMState == ATTACKING) // Failed to reach a waypoint - that's a failure condition whichever point we're at
if (m_EMState == CHASING)
{ {
ResetPathFinding(); // Try to calculate a path again. ResetPathFinding(); // Try to calculate a path again.
// This results in mobs hanging around an unreachable target (player). // This results in mobs hanging around an unreachable target (player).
@ -216,10 +188,8 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition. m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
} }
} }
else
{ m_IsFollowingPath = true;
m_NoMoreWayPoints = true;
}
return true; return true;
} }
} }
@ -389,8 +359,8 @@ bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
void cMonster::MoveToPosition(const Vector3d & a_Position) void cMonster::MoveToPosition(const Vector3d & a_Position)
{ {
m_FinalDestination = a_Position; m_FinalDestination = a_Position;
m_IsFollowingPath = true; m_PathfinderActivated = true;
} }
@ -399,7 +369,7 @@ void cMonster::MoveToPosition(const Vector3d & a_Position)
void cMonster::StopMovingToPosition() void cMonster::StopMovingToPosition()
{ {
m_IsFollowingPath = false; m_PathfinderActivated = false;
ResetPathFinding(); ResetPathFinding();
} }
@ -409,7 +379,7 @@ void cMonster::StopMovingToPosition()
void cMonster::ResetPathFinding(void) void cMonster::ResetPathFinding(void)
{ {
m_TicksSinceLastPathReset = 0; m_IsFollowingPath = false;
if (m_Path != nullptr) if (m_Path != nullptr)
{ {
delete m_Path; delete m_Path;

View File

@ -173,9 +173,11 @@ protected:
/** Stores if mobile is currently moving towards the ultimate, final destination */ /** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_IsFollowingPath; bool m_IsFollowingPath;
/** Stores if pathfinder is being used - set when final destination is set, and unset when stopped moving to final destination */
bool m_PathfinderActivated;
/* If 0, will give up reaching the next m_NextWayPointPosition and will re-compute path. */ /* If 0, will give up reaching the next m_NextWayPointPosition and will re-compute path. */
int m_GiveUpCounter; int m_GiveUpCounter;
int m_TicksSinceLastPathReset;
/** Coordinates of the next position that should be reached */ /** Coordinates of the next position that should be reached */
Vector3d m_NextWayPointPosition; Vector3d m_NextWayPointPosition;
@ -183,16 +185,6 @@ protected:
/** Coordinates for the ultimate, final destination. */ /** Coordinates for the ultimate, final destination. */
Vector3d m_FinalDestination; Vector3d m_FinalDestination;
/** Coordinates for the ultimate, final destination last given to the pathfinder. */
Vector3d m_PathFinderDestination;
/** True if there's no path to target and we're walking to an approximated location. */
bool m_NoPathToTarget;
/** Whether The mob has finished their path, note that this does not imply reaching the destination,
the destination may sometimes differ from the current path. */
bool m_NoMoreWayPoints;
/** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does) /** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1 If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that. If current Y is solid, goes up to find first nonsolid block, and returns that.

View File

@ -17,9 +17,6 @@
bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2) bool compareHeuristics::operator()(cPathCell * & a_Cell1, cPathCell * & a_Cell2)
{ {
return a_Cell1->m_F > a_Cell2->m_F; return a_Cell1->m_F > a_Cell2->m_F;
@ -36,7 +33,7 @@ cPath::cPath(
double a_BoundingBoxWidth, double a_BoundingBoxHeight, double a_BoundingBoxWidth, double a_BoundingBoxHeight,
int a_MaxUp, int a_MaxDown int a_MaxUp, int a_MaxDown
) : ) :
m_StepsLeft(a_MaxSteps),
m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint
m_Chunk(&a_Chunk), m_Chunk(&a_Chunk),
m_BadChunkFound(false) m_BadChunkFound(false)
@ -67,22 +64,8 @@ cPath::cPath(
m_NearestPointToTarget = GetCell(m_Source); m_NearestPointToTarget = GetCell(m_Source);
m_Status = ePathFinderStatus::CALCULATING; m_Status = ePathFinderStatus::CALCULATING;
m_StepsLeft = a_MaxSteps;
ProcessCell(GetCell(a_StartingPoint), nullptr, 0); ProcessCell(GetCell(m_Source), nullptr, 0);
m_Chunk = nullptr;
}
cPath::~cPath()
{
if (m_Status == ePathFinderStatus::CALCULATING)
{
FinishCalculation();
}
} }
@ -113,7 +96,7 @@ ePathFinderStatus cPath::Step(cChunk & a_Chunk)
int i; int i;
for (i = 0; i < CALCULATIONS_PER_STEP; ++i) for (i = 0; i < CALCULATIONS_PER_STEP; ++i)
{ {
if (Step_Internal()) // Step_Internal returns true when no more calculation is needed. if (StepOnce()) // StepOnce returns true when no more calculation is needed.
{ {
break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND. break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND.
} }
@ -179,7 +162,7 @@ bool cPath::IsSolid(const Vector3i & a_Location)
bool cPath::Step_Internal() bool cPath::StepOnce()
{ {
cPathCell * CurrentCell = OpenListPop(); cPathCell * CurrentCell = OpenListPop();

View File

@ -72,10 +72,7 @@ public:
double a_BoundingBoxWidth, double a_BoundingBoxHeight, double a_BoundingBoxWidth, double a_BoundingBoxHeight,
int a_MaxUp = 1, int a_MaxDown = 1 int a_MaxUp = 1, int a_MaxDown = 1
); );
/** Destroys the path and frees its memory. */
~cPath();
/** Performs part of the path calculation and returns the appropriate status. /** Performs part of the path calculation and returns the appropriate status.
If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found, is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
@ -99,45 +96,27 @@ public:
/** Checks whether this is the last point or not. Never call getnextPoint when this is true. */ /** Checks whether this is the last point or not. Never call getnextPoint when this is true. */
inline bool IsLastPoint() inline bool IsLastPoint() const
{ {
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
return (m_CurrentPoint == m_PathPoints.size() - 1); return (m_CurrentPoint == m_PathPoints.size() - 1);
} }
inline bool IsFirstPoint() const
inline bool IsFirstPoint()
{ {
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
return (m_CurrentPoint == 0); return (m_CurrentPoint == 0);
} }
/** Get the point at a_index. Remark: Internally, the indexes are reversed. */
inline Vector3d GetPoint(size_t a_index)
{
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
ASSERT(a_index < m_PathPoints.size());
Vector3i Point = m_PathPoints[m_PathPoints.size() - 1 - a_index];
return Vector3d(Point.x + m_HalfWidth, Point.y, Point.z + m_HalfWidth);
}
/** Returns the total number of points this path has. */
inline size_t GetPointCount()
{
if (m_Status != ePathFinderStatus::PATH_FOUND)
{
return 0;
}
return m_PathPoints.size();
}
private: private:
/* General */ /* General */
bool IsSolid(const Vector3i & a_Location); // Query our hosting world and ask it if there's a solid at a_location. bool IsSolid(const Vector3i & a_Location); // Query our hosting world and ask it if there's a solid at a_location.
bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times. bool StepOnce(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
void FinishCalculation(); // Clears the memory used for calculating the path. void FinishCalculation(); // Clears the memory used for calculating the path.
void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status. void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
void AttemptToFindAlternative(); void AttemptToFindAlternative();