Cuberite
A lightweight, fast and extensible game server for Minecraft
PathFinder.cpp
Go to the documentation of this file.
1 #include "Globals.h"
2 #include "PathFinder.h"
3 #include "../Chunk.h"
4 
5 
6 
7 
8 
9 cPathFinder::cPathFinder(double a_MobWidth, double a_MobHeight) :
10  m_Path(),
11  m_GiveUpCounter(0),
12  m_NotFoundCooldown(0)
13 {
14  m_Width = a_MobWidth;
15  m_Height = a_MobHeight;
16 }
17 
18 
19 
20 
21 
22 ePathFinderStatus cPathFinder::GetNextWayPoint(cChunk & a_Chunk, const Vector3d & a_Source, Vector3d * a_Destination, Vector3d * a_OutputWaypoint, bool a_DontCare)
23 {
24  m_FinalDestination = *a_Destination;
25  m_Source = a_Source;
26 
27  // If a recent PATH_NOT_FOUND was returned, we rest for a few ticks.
28  if (m_NotFoundCooldown > 0)
29  {
30  m_NotFoundCooldown -= 1;
32  }
33 
34  // Tweak the destination. If something is wrong with the destination or the chunk, rest for a while.
35  if (!(EnsureProperPoint(m_FinalDestination, a_Chunk) && EnsureProperPoint(m_Source, a_Chunk)))
36  {
37  m_NotFoundCooldown = 20;
39  }
40 
41  /* printf("%d %d %d -> %d %d %d\n",
42  static_cast<int>(m_Source.x),
43  static_cast<int>(m_Source.y),
44  static_cast<int>(m_Source.z),
45  static_cast<int>(m_FinalDestination.x),
46  static_cast<int>(m_FinalDestination.y),
47  static_cast<int>(m_FinalDestination.z)); */
48 
49  // Rest is over. Prepare m_Path by calling ResetPathFinding.
50  if (m_NotFoundCooldown == 0)
51  {
52  m_NotFoundCooldown = -1;
53  ResetPathFinding(a_Chunk);
54  }
55 
56  // If m_Path has not been initialized yet, initialize it.
57  if (!m_Path->IsValid())
58  {
59  ResetPathFinding(a_Chunk);
60  }
61 
62  switch (m_Path->CalculationStep(a_Chunk))
63  {
65  {
66  m_NoPathToTarget = true;
67  m_PathDestination = m_Path->AcceptNearbyPath();
68  if (a_DontCare)
69  {
71  *a_Destination = m_FinalDestination; // Modify the mob's final destination because it doesn't care about reaching an exact spot
72  }
73  else
74  {
75  m_DeviationOrigin = m_FinalDestination; // This is the only case in which m_DeviationOrigin != m_PathDestination
76  }
78  // The next call will trigger the PATH_FOUND case
79  }
80 
82  {
83  m_NotFoundCooldown = 20;
85  }
87  {
89  }
91  {
92  m_GiveUpCounter -= 1;
93 
94  if (m_GiveUpCounter == 0)
95  {
96  if (a_DontCare)
97  {
98  // We're having trouble reaching the next waypoint but the mob
99  // Doesn't care where to go, just tell him we got there ;)
101  *a_Destination = m_FinalDestination;
102  ResetPathFinding(a_Chunk);
104  }
105  else
106  {
107  ResetPathFinding(a_Chunk);
109  }
110  }
111 
112  if (PathIsTooOld())
113  {
114  ResetPathFinding(a_Chunk);
116  }
117 
118  if (m_Path->NoMoreWayPoints())
119  {
120  // We're always heading towards m_PathDestination.
121  // If m_PathDestination is exactly m_FinalDestination, then we're about to reach the destination.
123  {
124  *a_OutputWaypoint = m_FinalDestination;
126 
127  }
128  else
129  {
130  // Otherwise, we've finished our approximate path and time to recalc.
131  ResetPathFinding(a_Chunk);
133  }
134  }
135 
136  Vector3d Waypoint(m_WayPoint);
137  Vector3d Source(m_Source);
138  Waypoint.y = 0;
139  Source.y = 0;
140 
141  if (m_Path->IsFirstPoint() || (((Waypoint - Source).SqrLength() < WAYPOINT_RADIUS) && (m_Source.y >= m_WayPoint.y)))
142  {
143  // if the mob has just started or if the mob reached a waypoint, give them a new waypoint.
144  m_WayPoint = m_Path->GetNextPoint();
145  m_GiveUpCounter = 40;
147  }
148  else
149  {
150  // Otherwise, the mob is still walking towards its waypoint, we'll patiently wait. We won't update m_WayPoint.
151  *a_OutputWaypoint = m_WayPoint;
153  }
154  }
155  }
156  UNREACHABLE("Unsupported path finder status");
157 }
158 
159 
160 
161 
162 
164 {
165  m_GiveUpCounter = 40;
166  m_NoPathToTarget = false;
169  m_Path.reset(new cPath(a_Chunk, m_Source, m_PathDestination, 20, m_Width, m_Height));
170 }
171 
172 
173 
174 
175 
176 bool cPathFinder::EnsureProperPoint(Vector3d & a_Vector, cChunk & a_Chunk)
177 {
178  cChunk * Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x), FloorC(a_Vector.z));
179  BLOCKTYPE BlockType;
180  NIBBLETYPE BlockMeta;
181 
182  if ((Chunk == nullptr) || !Chunk->IsValid())
183  {
184  return false;
185  }
186 
187  int RelX = FloorC(a_Vector.x) - Chunk->GetPosX() * cChunkDef::Width;
188  int RelZ = FloorC(a_Vector.z) - Chunk->GetPosZ() * cChunkDef::Width;
189 
190  // If destination in the air, first try to go 1 block north, or east, or west.
191  // This fixes the player leaning issue.
192  // If that failed, we instead go down to the lowest air block.
193  int YBelowUs = FloorC(a_Vector.y) - 1;
194  if (YBelowUs < 0)
195  {
196  return false;
197 
198  }
199  Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
200  if (!(IsWaterOrSolid(BlockType)))
201  {
202  bool InTheAir = true;
203  int x, z;
204  for (z = -1; z <= 1; ++z)
205  {
206  for (x = -1; x <= 1; ++x)
207  {
208  if ((x == 0) && (z == 0))
209  {
210  continue;
211  }
212  Chunk = a_Chunk.GetNeighborChunk(FloorC(a_Vector.x+x), FloorC(a_Vector.z+z));
213  if ((Chunk == nullptr) || !Chunk->IsValid())
214  {
215  return false;
216  }
217  RelX = FloorC(a_Vector.x+x) - Chunk->GetPosX() * cChunkDef::Width;
218  RelZ = FloorC(a_Vector.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
219  Chunk->GetBlockTypeMeta(RelX, YBelowUs, RelZ, BlockType, BlockMeta);
220  if (IsWaterOrSolid((BlockType)))
221  {
222  a_Vector.x += x;
223  a_Vector.z += z;
224  InTheAir = false;
225  goto breakBothLoops;
226  }
227  }
228  }
229  breakBothLoops:
230 
231  // Go down to the lowest air block.
232  if (InTheAir)
233  {
234  while (a_Vector.y > 0)
235  {
236  Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y) - 1, RelZ, BlockType, BlockMeta);
237  if (IsWaterOrSolid(BlockType))
238  {
239  break;
240  }
241  a_Vector.y -= 1;
242  }
243  }
244  }
245 
246  // If destination in water or solid, go up to the first air block.
247  while (a_Vector.y < cChunkDef::Height)
248  {
249  Chunk->GetBlockTypeMeta(RelX, FloorC(a_Vector.y), RelZ, BlockType, BlockMeta);
250  if (!IsWaterOrSolid(BlockType))
251  {
252  break;
253  }
254  a_Vector.y += 1;
255  }
256 
257  return true;
258 }
259 
260 
261 
262 
263 
265 {
266  return ((a_BlockType == E_BLOCK_STATIONARY_WATER) || cBlockInfo::IsSolid(a_BlockType));
267 }
268 
269 
270 
271 
272 
274 {
275  size_t acceptableDeviation = m_Path->WayPointsLeft() / 2;
276  if (acceptableDeviation == 0)
277  {
278  acceptableDeviation = 1;
279  }
280  if ((m_FinalDestination - m_DeviationOrigin).SqrLength() > acceptableDeviation * acceptableDeviation)
281  {
282  return true;
283  }
284  return false;
285 }
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:72
Vector3d m_DeviationOrigin
When FinalDestination is too far from this, we recalculate.
Definition: PathFinder.h:66
T x
Definition: Vector3.h:17
static bool IsSolid(BLOCKTYPE a_Type)
Definition: BlockInfo.h:48
void GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Definition: Chunk.cpp:2230
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
double m_Height
The height of the Mob which owns this PathFinder.
Definition: PathFinder.h:47
static const int Width
Definition: ChunkDef.h:134
Definition: Path.h:67
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
Vector3d m_FinalDestination
Coordinates for where we should go.
Definition: PathFinder.h:59
Definition: Chunk.h:49
T y
Definition: Vector3.h:17
bool EnsureProperPoint(Vector3d &a_Vector, cChunk &a_Chunk)
Ensures the location is not in the air or under water.
Definition: PathFinder.cpp:176
T z
Definition: Vector3.h:17
double m_Width
The width of the Mob which owns this PathFinder.
Definition: PathFinder.h:44
static const int Height
Definition: ChunkDef.h:135
int m_GiveUpCounter
If 0, will give up reaching the next m_WayPoint and will recalculate path.
Definition: PathFinder.h:53
bool m_NoPathToTarget
True if there&#39;s no path to target and we&#39;re walking to a nearby location instead. ...
Definition: PathFinder.h:73
bool IsWaterOrSolid(BLOCKTYPE a_BlockType)
Return true the the blocktype is either water or solid.
Definition: PathFinder.cpp:264
#define WAYPOINT_RADIUS
Definition: PathFinder.h:5
Vector3d m_PathDestination
Coordinates for where we are practically going.
Definition: PathFinder.h:62
int GetPosX(void) const
Definition: Chunk.h:150
Vector3d m_WayPoint
Coordinates of the next position that should be reached.
Definition: PathFinder.h:56
ePathFinderStatus
Definition: Path.h:25
bool PathIsTooOld() const
Is the path too old and should be recalculated? When this is true ResetPathFinding() is called...
Definition: PathFinder.cpp:273
void ResetPathFinding(cChunk &a_Chunk)
Resets a pathfinding task, typically because m_FinalDestination has deviated too much from m_Deviatio...
Definition: PathFinder.cpp:163
ePathFinderStatus GetNextWayPoint(cChunk &a_Chunk, const Vector3d &a_Source, Vector3d *a_Destination, Vector3d *a_OutputWaypoint, bool a_DontCare=false)
Updates the PathFinder&#39;s internal state and returns a waypoint.
Definition: PathFinder.cpp:22
int m_NotFoundCooldown
When a path is not found, this cooldown prevents any recalculations for several ticks.
Definition: PathFinder.h:76
int GetPosZ(void) const
Definition: Chunk.h:151
cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ)
Returns the chunk into which the specified block belongs, by walking the neighbors.
Definition: Chunk.cpp:2279
Vector3d m_Source
Coordinates for where the mob is currently at.
Definition: PathFinder.h:70
std::unique_ptr< cPath > m_Path
The current cPath instance we have.
Definition: PathFinder.h:50
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default)
Definition: Globals.h:362
cPathFinder(double a_MobWidth, double a_MobHeight)
Creates a cPathFinder instance.
Definition: PathFinder.cpp:9
#define UNREACHABLE(x)
Use to mark code that should be impossible to reach.
Definition: Globals.h:344