Cuberite
A lightweight, fast and extensible game server for Minecraft
NetherPortalScanner.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
3 
4 #include "NetherPortalScanner.h"
5 #include "BlockInfo.h"
6 #include "Entities/Entity.h"
7 #include "World.h"
8 
9 
10 
11 
12 
13 const double cNetherPortalScanner::OutOffset = 0.5;
14 const double cNetherPortalScanner::AcrossOffset = 0.5;
15 
16 
17 
18 
19 
20 cNetherPortalScanner::cNetherPortalScanner(cEntity & a_MovingEntity, cWorld & a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY) :
21  m_EntityID(a_MovingEntity.GetUniqueID()),
22  m_SourceWorld(*a_MovingEntity.GetWorld()),
23  m_World(a_DestinationWorld),
24  m_FoundPortal(false),
25  m_BuildPlatform(true),
26  m_Dir(Direction::X),
27  m_PortalLoc(a_DestPosition.Floor()),
28  m_Position(a_DestPosition),
29  m_MaxY(a_MaxY)
30 {
35  for (int x = MinX; x < MaxX; x++)
36  {
37  for (int z = MinZ; z < MaxZ; z++)
38  {
39  Add(x, z);
40  }
41  }
42  Enable(*a_DestinationWorld.GetChunkMap());
43 }
44 
45 
46 
47 
48 
49 void cNetherPortalScanner::OnChunkAvailable(int a_ChunkX, int a_ChunkZ)
50 {
51  class PortalSearchCallback : public cChunkDataCallback
52  {
53  public:
54 
55  PortalSearchCallback(const int a_ChunkX, const int a_ChunkZ, bool & a_FoundPortal, Vector3i & a_PortalLoc, const Vector3d a_Position, const int a_MaxY) :
56  m_ChunkX(a_ChunkX),
57  m_ChunkZ(a_ChunkZ),
58  m_FoundPortal(a_FoundPortal),
59  m_PortalLoc(a_PortalLoc),
60  m_Position(a_Position),
61  m_MaxY(a_MaxY)
62  {
63  }
64 
65  private:
66 
67  virtual void ChunkData(const ChunkBlockData & a_BlockData, const ChunkLightData &) override
68  {
69  for (size_t Y = 0; Y < cChunkDef::NumSections; ++Y)
70  {
71  const auto Blocks = a_BlockData.GetSection(Y);
72  if (Blocks == nullptr)
73  {
74  continue;
75  }
76 
77  // Iterate through all of the blocks in the chunk:
78  for (size_t i = 0; i < ChunkBlockData::SectionBlockCount; i++)
79  {
80  if ((*Blocks)[i] != E_BLOCK_NETHER_PORTAL)
81  {
82  continue;
83  }
84 
86  if (Coordinate.y >= m_MaxY)
87  {
88  // This is above the map, don't consider it:
89  continue;
90  }
91 
92  Vector3d PortalLoc = Vector3d(Coordinate.x + m_ChunkX * cChunkDef::Width, Coordinate.y, Coordinate.z + m_ChunkZ * cChunkDef::Width);
93  if (!m_FoundPortal)
94  {
95  m_FoundPortal = true;
96  m_PortalLoc = PortalLoc;
97  }
98  else if ((PortalLoc - m_Position).SqrLength() < (m_PortalLoc - m_Position).SqrLength())
99  {
100  // Found a closer portal, use that instead:
101  m_PortalLoc = PortalLoc;
102  }
103  }
104  }
105  }
106 
107  int m_ChunkX, m_ChunkZ;
108 
109  bool & m_FoundPortal;
111  const Vector3d m_Position;
112 
113  const int m_MaxY;
114  } Callback(a_ChunkX, a_ChunkZ, m_FoundPortal, m_PortalLoc, m_Position, m_MaxY);
115 
116  [[maybe_unused]] const bool Result = m_World.GetChunkData({ a_ChunkX, a_ChunkZ }, Callback);
117  ASSERT(Result);
118 }
119 
120 
121 
122 
123 
125 {
126  // Check the base
127  for (int i = 0; i < SearchSolidBaseWidth; i++)
128  {
129  for (int j = 0; j < PortalLength; j++)
130  {
131  BLOCKTYPE blocktype = m_World.GetBlock(a_BlockPos.addedXZ(i, j));
132  if (!cBlockInfo::IsSolid(blocktype))
133  {
134  return false;
135  }
136 
137  // Check the airspace
138  for (int k = 1; k < PortalHeight; k++)
139  {
140  blocktype = m_World.GetBlock(a_BlockPos + Vector3i(i, k, j));
141  if (blocktype != E_BLOCK_AIR)
142  {
143  return false;
144  }
145  }
146  }
147  }
148  return true;
149 }
150 
151 
152 
153 
154 
156 {
157  if (m_FoundPortal)
158  {
159  // Find the bottom of this portal
161  {
162  m_PortalLoc.y -= 1;
163  }
164  m_PortalLoc.y += 1;
165 
166  // Figure out which way the portal is facing
167  int BXP = m_World.GetBlock(m_PortalLoc.addedX(1));
168  int BXM = m_World.GetBlock(m_PortalLoc.addedX(-1));
169  if ((BXP == E_BLOCK_NETHER_PORTAL) || (BXM == E_BLOCK_NETHER_PORTAL))
170  {
171  // The long axis is along X
173  }
174  else
175  {
176  // The long axis is along Z
178  }
179  }
180  else
181  {
182  // Scan the area for a suitable location
183  int minx = FloorC(m_Position.x) - BuildSearchRadius;
184  int minz = FloorC(m_Position.z) - BuildSearchRadius;
185  int maxx = FloorC(m_Position.x) + BuildSearchRadius;
186  int maxz = FloorC(m_Position.z) + BuildSearchRadius;
187  int maxy = m_MaxY;
188  std::vector<Vector3i> Possibilities;
189  int x, y, z;
190  for (y = 0; y < maxy - PortalHeight; y++)
191  {
192  for (x = minx; x < maxx - PortalLength; x++)
193  {
194  for (z = minz; z < maxz - SearchSolidBaseWidth; z++)
195  {
196  Vector3i Location = Vector3i(x, y, z);
197  if (IsValidBuildLocation(Location))
198  {
199  Possibilities.emplace_back(x, y, z);
200  }
201  }
202  }
203  }
204 
205  if (Possibilities.size() > 0)
206  {
207  m_BuildPlatform = false;
208 
209  // Find the nearest
210  double DistanceToClosest = (Possibilities[0] - m_Position).SqrLength();
211  Vector3i Closest = Possibilities[0];
212  for (const auto & itr : Possibilities)
213  {
214  double Distance = (itr - m_Position).SqrLength();
215  if (Distance < DistanceToClosest)
216  {
217  DistanceToClosest = Distance;
218  Closest = itr;
219  }
220  }
221 
222  m_PortalLoc = Closest;
223  }
224  }
225  return true;
226 }
227 
228 
229 
230 
231 
232 void cNetherPortalScanner::BuildNetherPortal(Vector3i a_Location, Direction a_Direction, bool a_IncludePlatform)
233 {
234  int x = a_Location.x;
235  int y = a_Location.y;
236  int z = a_Location.z;
237 
238  // Clear a 3x4x4 area starting right above the base
239  for (int i = 0; i < SearchSolidBaseWidth; i++)
240  {
241  for (int j = 0; j < PortalLength; j++)
242  {
243  for (int k = 1; k < PortalHeight; k++)
244  {
245  if (a_Direction == Direction::Y)
246  {
247  m_World.SetBlock({ x + i, y + k, z + j }, E_BLOCK_AIR, 0);
248  }
249  else if (a_Direction == Direction::X)
250  {
251  m_World.SetBlock({ x + j, y + k, z + i }, E_BLOCK_AIR, 0);
252  }
253  }
254  }
255  }
256 
257  // Put in an obsidian base
258  if (a_IncludePlatform)
259  {
260  for (int j = 0; j < PortalLength; j++)
261  {
262  // +2 on the short axis because that's where we deposit the entity
263  if (a_Direction == Direction::Y)
264  {
265  m_World.SetBlock({ x + 2, y, z + j }, E_BLOCK_OBSIDIAN, 0);
266  }
267  else if (a_Direction == Direction::X)
268  {
269  m_World.SetBlock({ x + j, y, z + 2 }, E_BLOCK_OBSIDIAN, 0);
270  }
271  }
272  }
273 
274  // Build an obsidian frame
275  for (int i = 0; i < PortalHeight; i++)
276  {
277  if (a_Direction == Direction::Y)
278  {
279  m_World.SetBlock({ x + 1, y + i, z }, E_BLOCK_OBSIDIAN, 0);
280  m_World.SetBlock({ x + 1, y + i, z + 3 }, E_BLOCK_OBSIDIAN, 0);
281  }
282  else if (a_Direction == Direction::X)
283  {
284  m_World.SetBlock({ x, y + i, z + 1 }, E_BLOCK_OBSIDIAN, 0);
285  m_World.SetBlock({ x + 3, y + i, z + 1 }, E_BLOCK_OBSIDIAN, 0);
286  }
287  }
288  for (int i = 0; i < PortalLength; i++)
289  {
290  if (a_Direction == Direction::Y)
291  {
292  m_World.SetBlock({ x + 1, y + 4, z + i }, E_BLOCK_OBSIDIAN, 0);
293  m_World.SetBlock({ x + 1, y, z + i }, E_BLOCK_OBSIDIAN, 0);
294  }
295  else if (a_Direction == Direction::X)
296  {
297  m_World.SetBlock({ x + i, y + 4, z + 1 }, E_BLOCK_OBSIDIAN, 0);
298  m_World.SetBlock({ x + i, y, z + 1 }, E_BLOCK_OBSIDIAN, 0);
299  }
300  }
301 
302  // Fill the frame (place a fire in the bottom)
303  m_World.PlaceBlock(Vector3<int>(x + 1, y + 1, z + 1), E_BLOCK_FIRE, 0);
304 }
305 
306 
307 
308 
309 
311 {
312  // Now we actually move the player
313  if (!m_FoundPortal)
314  {
315  // Build a new nether portal.
316  FLOGD("Building nether portal at {0}", m_PortalLoc);
318  m_PortalLoc.x += 1;
319  m_PortalLoc.y += 1;
320  m_PortalLoc.z += 1;
321  }
322 
323  // Put the entity near the opening
324  Vector3d Position = m_PortalLoc;
325  if (m_Dir == Direction::Y)
326  {
327  Position.x += OutOffset;
328  Position.z += AcrossOffset;
329  }
330  else if (m_Dir == Direction::X)
331  {
332  Position.x += AcrossOffset;
333  Position.z += OutOffset;
334  }
335 
336  auto EntityID = m_EntityID;
337  auto & DestinationWorld = m_World;
338  auto DestinationPosition = Position;
339 
340  // Lookup our warping entity by ID
341  // Necessary as they may have been destroyed in the meantime (#4582)
342  // And since this is called from the destination world's thread queue a task on the source world
344  [EntityID, &DestinationWorld, DestinationPosition](cWorld & a_World)
345  {
346  a_World.DoWithEntityByID(
347  EntityID,
348  [&DestinationWorld, &DestinationPosition](cEntity & a_Entity)
349  {
350  FLOGD("Placing player at {0}", DestinationPosition);
351  a_Entity.MoveToWorld(DestinationWorld, DestinationPosition, true, false);
352  return true;
353  }
354  );
355  }
356  );
357 
358  delete this;
359 }
360 
@ E_BLOCK_NETHER_PORTAL
Definition: BlockType.h:105
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_OBSIDIAN
Definition: BlockType.h:59
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
std::enable_if< std::is_arithmetic< T >::value, C >::type CeilC(T a_Value)
Ceils a value, then casts it to C (an int by default).
Definition: Globals.h:354
#define ASSERT(x)
Definition: Globals.h:276
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:347
#define FLOGD
Definition: LoggerSimple.h:91
Vector3< double > Vector3d
Definition: Vector3.h:485
Vector3< int > Vector3i
Definition: Vector3.h:487
unsigned char Distance(const BlockState Block)
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
static constexpr size_t SectionBlockCount
Definition: ChunkData.h:59
BlockArray * GetSection(size_t a_Y) const
Definition: ChunkData.h:83
static const int Width
Definition: ChunkDef.h:124
static const size_t NumSections
Definition: ChunkDef.h:129
static Vector3i IndexToCoordinate(size_t index)
Definition: ChunkDef.h:246
void Add(int a_ChunkX, int a_ChunkZ)
Adds a chunk to be locked from unloading.
Definition: ChunkStay.cpp:42
void Enable(cChunkMap &a_ChunkMap)
Enables the ChunkStay on the specified chunkmap, causing it to load and generate chunks.
Definition: ChunkStay.cpp:80
Definition: Entity.h:76
bool MoveToWorld(cWorld &a_World, Vector3d a_NewPosition, bool a_ShouldSetPortalCooldown=false, bool a_ShouldSendRespawn=true)
Definition: Entity.cpp:1616
Direction m_Dir
The direction of the portal.
UInt32 m_EntityID
The ID of the entity that's being moved.
int m_MaxY
The maximum Y to scan to.
cWorld & m_SourceWorld
The world we're moving the entity from, used to query the entity ID.
bool IsValidBuildLocation(Vector3i a_BlockPosition)
Whether the given location is a valid location to build a portal.
static const double OutOffset
Where to place the player out from the face and across the face.
static const int SearchSolidBaseWidth
The width of a solid base to search for when building.
virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkY) override
Called when a specific chunk become available.
virtual void OnDisabled(void) override
Called by the ChunkMap when the ChunkStay is disabled.
bool m_BuildPlatform
Whether to build a platform.
Vector3d m_Position
The center of the search area.
static const int PortalHeight
static const int PortalLength
Length and height, including the obsidian.
cWorld & m_World
The world we're moving the entity to.
static const int SearchRadius
static const double AcrossOffset
static const int BuildSearchRadius
void BuildNetherPortal(Vector3i a_Location, Direction a_Direction, bool a_IncludePlatform)
Builds a portal.
cNetherPortalScanner(cEntity &a_MovingEntity, cWorld &a_DestinationWorld, Vector3d a_DestPosition, int a_MaxY)
virtual bool OnAllChunksAvailable(void) override
Called once all of the contained chunks are available.
bool m_FoundPortal
Whether we found a portal during the loading of the chunks.
Vector3i m_PortalLoc
The position of the pre-existing portal.
T x
Definition: Vector3.h:17
Vector3< T > addedXZ(T a_AddX, T a_AddZ) const
Returns a copy of this vector moved by the specified amount on the X and Z axes.
Definition: Vector3.h:326
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Vector3< T > addedX(T a_AddX) const
Returns a copy of this vector moved by the specified amount on the X axis.
Definition: Vector3.h:308
Definition: World.h:53
BLOCKTYPE GetBlock(Vector3i a_BlockPos) const
Returns the block type at the specified position.
Definition: World.h:363
bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback &a_Callback) const
Calls the callback with the chunk's data, if available (with ChunkCS locked).
Definition: World.cpp:2202
bool DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback)
Calls the callback if the entity with the specified ID is found, with the entity object as the callba...
Definition: World.cpp:2464
void QueueTask(std::function< void(cWorld &)> a_Task)
Queues a task onto the tick thread.
Definition: World.cpp:2734
void PlaceBlock(const Vector3i a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
Replaces the specified block with another, and calls the OnPlaced block handler.
Definition: World.cpp:2043
cChunkMap * GetChunkMap(void)
Definition: World.h:852
void SetBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Sets the block at the specified coords to the specified value.
Definition: World.cpp:1743