Cuberite
A lightweight, fast and extensible game server for Minecraft
Chunk.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 #ifndef _WIN32
5  #include <cstdlib>
6 #endif
7 
8 
9 #include "Chunk.h"
10 #include "BlockInfo.h"
11 #include "World.h"
12 #include "ClientHandle.h"
13 #include "Server.h"
14 #include "Defines.h"
15 #include "Entities/Pickup.h"
16 #include "Item.h"
17 #include "Noise/Noise.h"
18 #include "Root.h"
19 #include "Entities/Player.h"
20 #include "BlockArea.h"
21 #include "Bindings/PluginManager.h"
22 #include "Blocks/BlockHandler.h"
25 #include "MobCensus.h"
26 #include "MobSpawner.h"
28 #include "SetChunkData.h"
29 #include "BoundingBox.h"
30 #include "Blocks/ChunkInterface.h"
31 
32 #include "json/json.h"
33 
34 
35 
36 
37 
39 // cChunk:
40 
42  int a_ChunkX, int a_ChunkZ,
43  cChunkMap * a_ChunkMap, cWorld * a_World
44 ):
45  m_Presence(cpInvalid),
46  m_IsLightValid(false),
47  m_IsDirty(false),
48  m_IsSaving(false),
49  m_StayCount(0),
50  m_PosX(a_ChunkX),
51  m_PosZ(a_ChunkZ),
52  m_World(a_World),
53  m_ChunkMap(a_ChunkMap),
54  m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()),
55  m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()),
56  m_RedstoneSimulatorData(a_World->GetRedstoneSimulator()->CreateChunkData()),
57  m_AlwaysTicked(0)
58 {
59  m_NeighborXM = a_ChunkMap->FindChunk(a_ChunkX - 1, a_ChunkZ);
60  m_NeighborXP = a_ChunkMap->FindChunk(a_ChunkX + 1, a_ChunkZ);
61  m_NeighborZM = a_ChunkMap->FindChunk(a_ChunkX, a_ChunkZ - 1);
62  m_NeighborZP = a_ChunkMap->FindChunk(a_ChunkX, a_ChunkZ + 1);
63 
64  if (m_NeighborXM != nullptr)
65  {
66  m_NeighborXM->m_NeighborXP = this;
67  }
68  if (m_NeighborXP != nullptr)
69  {
70  m_NeighborXP->m_NeighborXM = this;
71  }
72  if (m_NeighborZM != nullptr)
73  {
74  m_NeighborZM->m_NeighborZP = this;
75  }
76  if (m_NeighborZP != nullptr)
77  {
78  m_NeighborZP->m_NeighborZM = this;
79  }
80 }
81 
82 
83 
84 
85 
87 {
88  // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId());
89 
90  // Inform our neighbours that we're no longer valid:
91  if (m_NeighborXM != nullptr)
92  {
93  m_NeighborXM->m_NeighborXP = nullptr;
94  }
95  if (m_NeighborXP != nullptr)
96  {
97  m_NeighborXP->m_NeighborXM = nullptr;
98  }
99  if (m_NeighborZM != nullptr)
100  {
101  m_NeighborZM->m_NeighborZP = nullptr;
102  }
103  if (m_NeighborZP != nullptr)
104  {
105  m_NeighborZP->m_NeighborZM = nullptr;
106  }
107 
108  delete m_WaterSimulatorData;
109  m_WaterSimulatorData = nullptr;
110  delete m_LavaSimulatorData;
111  m_LavaSimulatorData = nullptr;
113  m_RedstoneSimulatorData = nullptr;
114 }
115 
116 
117 
118 
119 
121 {
122  if (const auto PendingBlocksCount = m_PendingSendBlocks.size(); PendingBlocksCount >= 10240)
123  {
124  // Resend the full chunk:
125  for (const auto ClientHandle : m_LoadedByClient)
126  {
128  }
129  }
130  else if (PendingBlocksCount == 0)
131  {
132  // Only send block entity changes:
133  for (const auto ClientHandle : m_LoadedByClient)
134  {
135  for (const auto BlockEntity : m_PendingSendBlockEntities)
136  {
137  BlockEntity->SendTo(*ClientHandle);
138  }
139  }
140  }
141  else
142  {
143  // Send block and block entity changes:
144  for (const auto ClientHandle : m_LoadedByClient)
145  {
146  ClientHandle->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks);
147 
148  for (const auto BlockEntity : m_PendingSendBlockEntities)
149  {
150  BlockEntity->SendTo(*ClientHandle);
151  }
152  }
153  }
154 
155  m_PendingSendBlocks.clear();
157 }
158 
159 
160 
161 
162 
164 {
165  m_Presence = a_Presence;
166  if (a_Presence == cpPresent)
167  {
169  }
170 }
171 
172 
173 
174 
175 
177 {
178  // Set as queued again:
180 
181  // Tell all clients attached to this chunk that they want this chunk:
182  for (auto ClientHandle : m_LoadedByClient)
183  {
184  ClientHandle->AddWantedChunk(m_PosX, m_PosZ);
185  } // for itr - m_LoadedByClient[]
186 }
187 
188 
189 
190 
191 
193 {
194  return std::any_of(
195  m_Entities.begin(), m_Entities.end(),
196  [](const auto & Entity)
197  {
198  return Entity->IsPlayer();
199  }
200  );
201 }
202 
203 
204 
205 
206 
207 bool cChunk::CanUnload(void) const
208 {
209  return
210  m_LoadedByClient.empty() && // The chunk is not used by any client
211  !HasPlayerEntities() && // Ensure not only the absence of ClientHandlers, but also of cPlayer objects
212  !m_IsDirty && // The chunk has been saved properly or hasn't been touched since the load / gen
213  (m_StayCount == 0) && // The chunk is not in a ChunkStay
214  (m_Presence != cpQueued) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur)
215 }
216 
217 
218 
219 
220 
222 {
223  return
224  m_LoadedByClient.empty() && // The chunk is not used by any client
225  !HasPlayerEntities() && // Ensure not only the absence of ClientHandlers, but also of cPlayer objects
226  m_IsDirty && // The chunk is dirty
227  (m_StayCount == 0) && // The chunk is not in a ChunkStay
228  (m_Presence != cpQueued) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur)
229 }
230 
231 
232 
233 
234 
236 {
237  // Note: this is only called during normal operation, not during shutdown
238 
239  // Notify all entities of imminent unload:
240  for (auto & Entity : m_Entities)
241  {
242  // Chunks cannot be unloaded when they still contain players:
243  ASSERT(!Entity->IsPlayer());
244 
245  // Notify the entity:
246  Entity->OnRemoveFromWorld(*Entity->GetWorld());
247  }
248 
249  // Notify all block entities of imminent unload:
250  for (auto & KeyPair : m_BlockEntities)
251  {
252  KeyPair.second->OnRemoveFromWorld();
253  }
254 }
255 
256 
257 
258 
259 
261 {
262  m_IsSaving = true;
263 }
264 
265 
266 
267 
268 
270 {
271  if (!m_IsSaving)
272  {
273  return;
274  }
275  m_IsDirty = false;
276 }
277 
278 
279 
280 
281 
283 {
284  m_IsDirty = false;
286 }
287 
288 
289 
290 
291 
293 {
295 
296  // Mark dirty before generating, so that we get saved and don't have to later generate again:
297  MarkDirty();
298 
299  // The chunk is always needed, generate it:
301 }
302 
303 
304 
305 
306 
307 void cChunk::GetAllData(cChunkDataCallback & a_Callback) const
308 {
310 
311  a_Callback.LightIsValid(m_IsLightValid);
312  a_Callback.ChunkData(m_BlockData, m_LightData);
313  a_Callback.HeightMap(m_HeightMap);
314  a_Callback.BiomeMap(m_BiomeMap);
315 
316  for (const auto & Entity : m_Entities)
317  {
318  a_Callback.Entity(Entity.get());
319  }
320 
321  for (auto & KeyPair : m_BlockEntities)
322  {
323  a_Callback.BlockEntity(KeyPair.second.get());
324  }
325 }
326 
327 
328 
329 
330 
331 void cChunk::SetAllData(SetChunkData && a_SetChunkData)
332 {
333  std::copy_n(a_SetChunkData.HeightMap, std::size(a_SetChunkData.HeightMap), m_HeightMap);
334  std::copy_n(a_SetChunkData.BiomeMap, std::size(a_SetChunkData.BiomeMap), m_BiomeMap);
335 
336  m_BlockData = std::move(a_SetChunkData.BlockData);
337  m_LightData = std::move(a_SetChunkData.LightData);
338  m_IsLightValid = a_SetChunkData.IsLightValid;
339 
340  m_PendingSendBlocks.clear();
342 
343  // Entities need some extra steps to destroy, so here we're keeping the old ones.
344  // Move the entities already in the chunk, including player entities, so that we don't lose any:
345  a_SetChunkData.Entities.insert(
346  a_SetChunkData.Entities.end(),
347  std::make_move_iterator(m_Entities.begin()),
348  std::make_move_iterator(m_Entities.end())
349  );
350 
351  // Store the augmented result:
352  m_Entities = std::move(a_SetChunkData.Entities);
353 
354  // Set all the entity variables again:
355  for (const auto & Entity : m_Entities)
356  {
357  Entity->SetWorld(m_World);
358  Entity->SetParentChunk(this);
359  Entity->SetIsTicking(true);
360  }
361 
362  // Remove the block entities present - either the loader / saver has better, or we'll create empty ones:
363  for (auto & KeyPair : m_BlockEntities)
364  {
365  KeyPair.second->Destroy();
366  KeyPair.second->OnRemoveFromWorld();
367  }
368 
369  // Clear the old ones:
370  m_BlockEntities = std::move(a_SetChunkData.BlockEntities);
371 
372  // Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only):
373 #ifndef NDEBUG
374  for (auto & KeyPair : m_BlockEntities)
375  {
376  cBlockEntity * Block = KeyPair.second.get();
377  BLOCKTYPE EntityBlockType = Block->GetBlockType();
378  BLOCKTYPE WorldBlockType = GetBlock(Block->GetRelX(), Block->GetPosY(), Block->GetRelZ());
379  ASSERT(WorldBlockType == EntityBlockType);
380  }
381 #endif
382 
383  // Set the chunk data as valid.
384  // This may be needed for some simulators that perform actions upon block adding (Vaporize),
385  // as well as some block entities upon being added to the chunk (Chests).
387 
388  // Initialise all block entities:
389  for (auto & KeyPair : m_BlockEntities)
390  {
391  KeyPair.second->OnAddToWorld(*m_World, *this);
392  }
393 
394  // Wake up all simulators for their respective blocks:
396 }
397 
398 
399 
400 
401 
403  const cChunkDef::BlockNibbles & a_BlockLight,
404  const cChunkDef::BlockNibbles & a_SkyLight
405 )
406 {
407  // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
408  // Postponing until we see how bad it is :)
409 
410  m_LightData.SetAll(a_BlockLight, a_SkyLight);
411 
412  MarkDirty();
413  m_IsLightValid = true;
414 }
415 
416 
417 
418 
419 
420 void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
421 {
423  {
424  LOGWARNING("cChunk::WriteBlockArea(): unsupported datatype request, can write only types + metas together (0x%x), requested 0x%x. Ignoring.",
426  );
427  return;
428  }
429 
430  // SizeX, SizeZ are the dimensions of the block data to copy to the chunk (size of the geometric union)
431 
432  int BlockStartX = std::max(a_MinBlockX, m_PosX * cChunkDef::Width);
433  int BlockEndX = std::min(a_MinBlockX + a_Area.GetSizeX(), (m_PosX + 1) * cChunkDef::Width);
434  int BlockStartZ = std::max(a_MinBlockZ, m_PosZ * cChunkDef::Width);
435  int BlockEndZ = std::min(a_MinBlockZ + a_Area.GetSizeZ(), (m_PosZ + 1) * cChunkDef::Width);
436  int SizeX = BlockEndX - BlockStartX; // Size of the union
437  int SizeZ = BlockEndZ - BlockStartZ;
438  int SizeY = std::min(a_Area.GetSizeY(), cChunkDef::Height - a_MinBlockY);
439  int OffX = BlockStartX - m_PosX * cChunkDef::Width; // Offset within the chunk where the union starts
440  int OffZ = BlockStartZ - m_PosZ * cChunkDef::Width;
441  int BaseX = BlockStartX - a_MinBlockX; // Offset within the area where the union starts
442  int BaseZ = BlockStartZ - a_MinBlockZ;
443 
444  // Copy blocktype and blockmeta:
445  BLOCKTYPE * AreaBlockTypes = a_Area.GetBlockTypes();
446  NIBBLETYPE * AreaBlockMetas = a_Area.GetBlockMetas();
447  for (int y = 0; y < SizeY; y++)
448  {
449  int ChunkY = a_MinBlockY + y;
450  int AreaY = y;
451  for (int z = 0; z < SizeZ; z++)
452  {
453  int ChunkZ = OffZ + z;
454  int AreaZ = BaseZ + z;
455  for (int x = 0; x < SizeX; x++)
456  {
457  int ChunkX = OffX + x;
458  int AreaX = BaseX + x;
459  auto idx = a_Area.MakeIndex(AreaX, AreaY, AreaZ);
460  BLOCKTYPE BlockType = AreaBlockTypes[idx];
461  NIBBLETYPE BlockMeta = AreaBlockMetas[idx];
462  FastSetBlock(ChunkX, ChunkY, ChunkZ, BlockType, BlockMeta);
463  } // for x
464  } // for z
465  } // for y
466 
467  // Erase all affected block entities:
468  {
469  // The affected area, in world coordinates.
470  cCuboid affectedArea(
471  { BlockStartX, a_MinBlockY, BlockStartZ },
472  { BlockEndX, a_MinBlockY + SizeY - 1, BlockEndZ }
473  );
474 
475  // Where in the pending block entity send list to start removing the invalidated elements from.
476  auto PendingRemove = m_PendingSendBlockEntities.end();
477 
478  for (auto itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();)
479  {
480  if (affectedArea.IsInside(itr->second->GetPos()))
481  {
482  itr->second->Destroy();
483  itr->second->OnRemoveFromWorld();
484 
485  PendingRemove = std::remove(m_PendingSendBlockEntities.begin(), PendingRemove, itr->second.get()); // Search the remaining valid pending sends.
486  itr = m_BlockEntities.erase(itr);
487  }
488  else
489  {
490  ++itr;
491  }
492  }
493 
494  // Remove all the deleted block entities from the pending send list:
495  m_PendingSendBlockEntities.erase(PendingRemove, m_PendingSendBlockEntities.end());
496  }
497 
498  // Clone block entities from a_Area into this chunk:
499  if ((a_DataTypes & cBlockArea::baBlockEntities) != 0)
500  {
501  for (const auto & keyPair: a_Area.GetBlockEntities())
502  {
503  auto & be = keyPair.second;
504  auto posX = be->GetPosX() + a_MinBlockX;
505  auto posY = be->GetPosY() + a_MinBlockY;
506  auto posZ = be->GetPosZ() + a_MinBlockZ;
507  if (
508  (posX < m_PosX * cChunkDef::Width) || (posX >= m_PosX * cChunkDef::Width + cChunkDef::Width) ||
510  )
511  {
512  continue;
513  }
514 
515  // This block entity is inside the chunk.
516  // The code above should have removed any that were here before:
517  ASSERT(GetBlockEntityRel(cChunkDef::AbsoluteToRelative({ posX, posY, posZ })) == nullptr);
518 
519  // Clone, and add the new one:
520  AddBlockEntity(be->Clone({posX, posY, posZ}));
521  }
522  }
523 }
524 
525 
526 
527 
528 
529 void cChunk::Stay(bool a_Stay)
530 {
531  if (a_Stay)
532  {
533  m_StayCount++;
534  }
535  else
536  {
537  ASSERT(m_StayCount != 0);
538  m_StayCount--;
539  }
540 }
541 
542 
543 
544 
545 
547 {
548  toFill.CollectSpawnableChunk(*this);
549  std::vector<Vector3d> PlayerPositions;
550  PlayerPositions.reserve(m_LoadedByClient.size());
551  for (auto ClientHandle : m_LoadedByClient)
552  {
553  const cPlayer * currentPlayer = ClientHandle->GetPlayer();
554  PlayerPositions.push_back(currentPlayer->GetPosition());
555  }
556 
557  Vector3d currentPosition;
558  for (auto & entity : m_Entities)
559  {
560  // LOGD("Counting entity #%i (%s)", (*itr)->GetUniqueID(), (*itr)->GetClass());
561  if (entity->IsMob())
562  {
563  auto & Monster = static_cast<cMonster &>(*entity);
564  currentPosition = Monster.GetPosition();
565  for (const auto & PlayerPos : PlayerPositions)
566  {
567  toFill.CollectMob(Monster, *this, (currentPosition - PlayerPos).SqrLength());
568  }
569  }
570  } // for itr - m_Entitites[]
571 }
572 
573 
574 
575 
576 
577 void cChunk::GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z, int a_MaxX, int a_MaxY, int a_MaxZ)
578 {
579  ASSERT(
580  (a_MaxX > 0) && (a_MaxY > 0) && (a_MaxZ > 0) &&
581  (a_MaxX <= std::numeric_limits<int>::max() / a_MaxY) && // a_MaxX * a_MaxY doesn't overflow
582  (a_MaxX * a_MaxY <= std::numeric_limits<int>::max() / a_MaxZ) // a_MaxX * a_MaxY * a_MaxZ doesn't overflow
583  );
584 
585  // MTRand gives an inclusive range [0, Max] but this gives the exclusive range [0, Max)
586  int OverallMax = (a_MaxX * a_MaxY * a_MaxZ) - 1;
587  int Random = m_World->GetTickRandomNumber(OverallMax);
588 
589  a_X = Random % a_MaxX;
590  a_Y = (Random / a_MaxX) % a_MaxY;
591  a_Z = ((Random / a_MaxX) / a_MaxY) % a_MaxZ;
592 }
593 
594 
595 
596 
597 
598 void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z)
599 {
600  // MG TODO : check if this kind of optimization (only one random call) is still needed
601  // MG TODO : if so propagate it
602 
604  a_Y++;
605 }
606 
607 
608 
609 
610 
611 void cChunk::SpawnMobs(cMobSpawner & a_MobSpawner)
612 {
613  int CenterX, CenterY, CenterZ;
614  GetRandomBlockCoords(CenterX, CenterY, CenterZ);
615 
616  BLOCKTYPE PackCenterBlock = GetBlock(CenterX, CenterY, CenterZ);
617  if (!a_MobSpawner.CheckPackCenter(PackCenterBlock))
618  {
619  return;
620  }
621 
622  a_MobSpawner.NewPack();
623  int NumberOfTries = 0;
624  int NumberOfSuccess = 0;
625  int MaxNbOfSuccess = 4; // This can be changed during the process for Wolves and Ghasts
626  while ((NumberOfTries < 12) && (NumberOfSuccess < MaxNbOfSuccess))
627  {
628  const int HorizontalRange = 20; // MG TODO : relocate
629  const int VerticalRange = 0; // MG TODO : relocate
630  int TryX, TryY, TryZ;
631  GetThreeRandomNumbers(TryX, TryY, TryZ, 2 * HorizontalRange + 1, 2 * VerticalRange + 1, 2 * HorizontalRange + 1);
632  TryX -= HorizontalRange;
633  TryY -= VerticalRange;
634  TryZ -= HorizontalRange;
635  TryX += CenterX;
636  TryY += CenterY;
637  TryZ += CenterZ;
638 
639  ASSERT(TryY > 0);
640  ASSERT(TryY < cChunkDef::Height - 1);
641 
642  int WorldX, WorldY, WorldZ;
643  PositionToWorldPosition(TryX, TryY, TryZ, WorldX, WorldY, WorldZ);
644 
645  // MG TODO :
646  // Moon cycle (for slime)
647  // check player and playerspawn presence < 24 blocks
648  // check mobs presence on the block
649 
650  // MG TODO : check that "Level" really means Y
651 
652  /*
653  NIBBLETYPE SkyLight = 0;
654 
655  NIBBLETYPE BlockLight = 0;
656  */
657 
658  NumberOfTries++;
659 
660  Vector3i Try(TryX, TryY, TryZ);
661  const auto Chunk = GetRelNeighborChunkAdjustCoords(Try);
662 
663  if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid())
664  {
665  continue;
666  }
667 
668  auto newMob = a_MobSpawner.TryToSpawnHere(this, Try, GetBiomeAt(Try.x, Try.z), MaxNbOfSuccess);
669  if (newMob == nullptr)
670  {
671  continue;
672  }
673  double ActualX = WorldX + 0.5;
674  double ActualZ = WorldZ + 0.5;
675  newMob->SetPosition(ActualX, WorldY, ActualZ);
676  FLOGD("Spawning {0} #{1} at {2}", newMob->GetClass(), newMob->GetUniqueID(), Vector3i{WorldX, WorldY, WorldZ});
677  NumberOfSuccess++;
678  } // while (retry)
679 }
680 
681 
682 
683 
684 
685 void cChunk::Tick(std::chrono::milliseconds a_Dt)
686 {
687  TickBlocks();
688 
689  // Tick all block entities in this chunk:
690  for (auto & KeyPair : m_BlockEntities)
691  {
692  m_IsDirty = KeyPair.second->Tick(a_Dt, *this) | m_IsDirty;
693  }
694 
695  for (auto itr = m_Entities.begin(); itr != m_Entities.end();)
696  {
697  // Do not tick mobs that are detached from the world. They're either scheduled for teleportation or for removal.
698  if (!(*itr)->IsTicking())
699  {
700  ++itr;
701  continue;
702  }
703 
704  if (!((*itr)->IsMob())) // Mobs are ticked inside cWorld::TickMobs() (as we don't have to tick them if they are far away from players)
705  {
706  // Tick all entities in this chunk (except mobs):
707  ASSERT((*itr)->GetParentChunk() == this);
708  (*itr)->Tick(a_Dt, *this);
709  ASSERT((*itr)->GetParentChunk() == this);
710  }
711 
712  // Do not move mobs that are detached from the world to neighbors. They're either scheduled for teleportation or for removal.
713  // Because the schedulded destruction is going to look for them in this chunk. See cEntity::destroy.
714  if (!(*itr)->IsTicking())
715  {
716  ++itr;
717  continue;
718  }
719 
720  if (
721  ((*itr)->GetChunkX() != m_PosX) ||
722  ((*itr)->GetChunkZ() != m_PosZ)
723  )
724  {
725  // Mark as dirty if it was a server-generated entity:
726  if (!(*itr)->IsPlayer())
727  {
728  MarkDirty();
729  }
730 
731  // This block is very similar to RemoveEntity, except it uses an iterator to avoid scanning the whole m_Entities
732  // The entity moved out of the chunk, move it to the neighbor
733  (*itr)->SetParentChunk(nullptr);
734  MoveEntityToNewChunk(std::move(*itr));
735 
736  itr = m_Entities.erase(itr);
737  }
738  else
739  {
740  ++itr;
741  }
742  } // for itr - m_Entitites[]
743 
745 
746  // Tick simulators:
748 
749  // Check blocks after everything else to apply at least one round of queued ticks (i.e. cBlockHandler::Check) this tick:
750  CheckBlocks();
751 }
752 
753 
754 
755 
756 
757 void cChunk::TickBlock(const Vector3i a_RelPos)
758 {
759  cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
760  cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
761  cBlockHandler::For(GetBlock(a_RelPos)).OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, a_RelPos);
762 }
763 
764 
765 
766 
767 
769 {
770  cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width);
771  if (Neighbor == nullptr)
772  {
773  LOGWARNING("%s: Entity at %p (%s, ID %d) moving to a non-existent chunk.",
774  __FUNCTION__, static_cast<void *>(a_Entity.get()), a_Entity->GetClass(), a_Entity->GetUniqueID()
775  );
776 
777  Neighbor = &m_ChunkMap->ConstructChunk(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
778  }
779 
780  ASSERT(Neighbor != this); // Moving into the same chunk? wtf?
781  auto & Entity = *a_Entity;
782  Neighbor->AddEntity(std::move(a_Entity));
783 
784  class cMover :
785  public cClientDiffCallback
786  {
787  virtual void Removed(cClientHandle * a_Client) override
788  {
789  a_Client->SendDestroyEntity(m_Entity);
790  }
791 
792  virtual void Added(cClientHandle * a_Client) override
793  {
794  m_Entity.SpawnOn(*a_Client);
795  }
796 
797  cEntity & m_Entity;
798 
799  public:
800  cMover(cEntity & a_CallbackEntity) :
801  m_Entity(a_CallbackEntity)
802  {}
803  } Mover(Entity);
804 
805  m_ChunkMap->CompareChunkClients(this, Neighbor, Mover);
806 }
807 
808 
809 
810 
811 
813 {
814  cChunkInterface ChunkInterface(m_World->GetChunkMap());
815  cBlockInServerPluginInterface PluginInterface(*m_World);
816 
817  // Process a limited number of blocks since cBlockHandler::Check may queue more to tick
818  auto Count = m_BlocksToCheck.size();
819 
820  while (Count != 0)
821  {
822  const auto Pos = m_BlocksToCheck.front();
823  m_BlocksToCheck.pop();
824  Count--;
825 
826  cBlockHandler::For(GetBlock(Pos)).Check(ChunkInterface, PluginInterface, Pos, *this);
827  }
828 }
829 
830 
831 
832 
833 
835 {
836  cChunkInterface ChunkInterface(m_World->GetChunkMap());
837  cBlockInServerPluginInterface PluginInterface(*m_World);
838 
839  // Tick random blocks, but the first one should be m_BlockToTick (so that SetNextBlockToTick() works):
840  cBlockHandler::For(GetBlock(m_BlockToTick)).OnUpdate(ChunkInterface, *m_World, PluginInterface, *this, m_BlockToTick);
841 
842  auto & Random = GetRandomProvider();
843 
844  // Set a new random coord for the next tick:
845  m_BlockToTick = cChunkDef::IndexToCoordinate(Random.RandInt<size_t>(cChunkDef::NumBlocks - 1));
846 
847  // Choose a number of blocks for each section to randomly tick.
848  // http://minecraft.wiki/w/Tick#Random_tick
849  for (size_t Y = 0; Y < cChunkDef::NumSections; ++Y)
850  {
851  const auto Section = m_BlockData.GetSection(Y);
852  if (Section == nullptr)
853  {
854  continue;
855  }
856 
857  for (int Tick = 0; Tick != 3; Tick++) // TODO: configurability via gamerule randomTickSpeed
858  {
859  const auto Index = Random.RandInt<size_t>(ChunkBlockData::SectionBlockCount - 1);
860  const auto Position = cChunkDef::IndexToCoordinate(Y * ChunkBlockData::SectionBlockCount + Index);
861 
862  cBlockHandler::For((*Section)[Index]).OnUpdate(ChunkInterface, *m_World, PluginInterface, *this, Position);
863  }
864  }
865 }
866 
867 
868 
869 
870 
872 {
873  if (
874  (GetRandomProvider().RandBool(0.99)) ||
875  (
876  (m_World->GetWeather() != eWeather_Rain) &&
878  )
879  )
880  {
881  // Not the right weather, or not at this tick; bail out
882  return;
883  }
884 
885  int X = m_World->GetTickRandomNumber(15);
886  int Z = m_World->GetTickRandomNumber(15);
887 
888  int Height = GetHeight(X, Z);
889 
890  if (GetSnowStartHeight(GetBiomeAt(X, Z)) > Height)
891  {
892  return;
893  }
894 
895  if (GetBlockLight(X, Height, Z) > 10)
896  {
897  // Snow only generates on blocks with a block light level of 10 or less.
898  // Ref: https://minecraft.wiki/w/Snow_(layer)#Snowfall
899  return;
900  }
901 
902  BLOCKTYPE TopBlock = GetBlock(X, Height, Z);
903  NIBBLETYPE TopMeta = GetMeta (X, Height, Z);
904  if (m_World->IsDeepSnowEnabled() && (TopBlock == E_BLOCK_SNOW))
905  {
906  int MaxSize = 7;
907  BLOCKTYPE BlockType[4];
908  NIBBLETYPE BlockMeta[4];
909  UnboundedRelGetBlock(X - 1, Height, Z, BlockType[0], BlockMeta[0]);
910  UnboundedRelGetBlock(X + 1, Height, Z, BlockType[1], BlockMeta[1]);
911  UnboundedRelGetBlock(X, Height, Z - 1, BlockType[2], BlockMeta[2]);
912  UnboundedRelGetBlock(X, Height, Z + 1, BlockType[3], BlockMeta[3]);
913  for (int i = 0; i < 4; i++)
914  {
915  switch (BlockType[i])
916  {
917  case E_BLOCK_AIR:
918  {
919  MaxSize = 0;
920  break;
921  }
922  case E_BLOCK_SNOW:
923  {
924  MaxSize = std::min(BlockMeta[i] + 1, MaxSize);
925  break;
926  }
927  }
928  }
929  if (TopMeta < MaxSize)
930  {
931  FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta + 1);
932  }
933  else if (TopMeta > MaxSize)
934  {
935  FastSetBlock(X, Height, Z, E_BLOCK_SNOW, TopMeta - 1);
936  }
937  }
938  else if (cBlockInfo::IsSnowable(TopBlock) && (Height < cChunkDef::Height - 1))
939  {
940  SetBlock({X, Height + 1, Z}, E_BLOCK_SNOW, 0);
941  }
942  else if (IsBlockWater(TopBlock) && (TopMeta == 0))
943  {
944  SetBlock({X, Height, Z}, E_BLOCK_ICE, 0);
945  }
946  else if (
947  (m_World->IsDeepSnowEnabled()) &&
948  (
949  (TopBlock == E_BLOCK_RED_ROSE) ||
950  (TopBlock == E_BLOCK_YELLOW_FLOWER) ||
951  (TopBlock == E_BLOCK_RED_MUSHROOM) ||
952  (TopBlock == E_BLOCK_BROWN_MUSHROOM)
953  )
954  )
955  {
956  SetBlock({X, Height, Z}, E_BLOCK_SNOW, 0);
957  }
958 }
959 
960 
961 
962 
963 
964 cItems cChunk::PickupsFromBlock(Vector3i a_RelPos, const cEntity * a_Digger, const cItem * a_Tool)
965 {
967  NIBBLETYPE BlockMeta;
968  GetBlockTypeMeta(a_RelPos, BlockType, BlockMeta);
969 
970  cItems Pickups;
971  const auto BlockEntity = GetBlockEntityRel(a_RelPos);
972 
973  if ((a_Tool == nullptr) || a_Tool->GetHandler().CanHarvestBlock(BlockType))
974  {
975  Pickups = cBlockHandler::For(BlockType).ConvertToPickups(BlockMeta, a_Tool);
976 
977  if (BlockEntity != nullptr)
978  {
979  auto BlockEntityPickups = BlockEntity->ConvertToPickups();
980  Pickups.insert(Pickups.end(), std::make_move_iterator(BlockEntityPickups.begin()), std::make_move_iterator(BlockEntityPickups.end()));
981  }
982  }
983 
984  // TODO: this should be in cWorld::DropBlockAsPickups. When it's here we can't check the return value and cancel spawning:
986  *m_World,
988  BlockType, BlockMeta, BlockEntity,
989  a_Digger, a_Tool, Pickups
990  );
991 
992  return Pickups;
993 }
994 
995 
996 
997 
998 
999 int cChunk::GrowPlantAt(Vector3i a_RelPos, int a_NumStages)
1000 {
1001  return cBlockHandler::For(GetBlock(a_RelPos)).Grow(*this, a_RelPos, a_NumStages);
1002 }
1003 
1004 
1005 
1006 
1007 
1008 bool cChunk::UnboundedRelGetBlock(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
1009 {
1010  if (!cChunkDef::IsValidHeight(a_RelPos))
1011  {
1012  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1013  return false;
1014  }
1015  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1016  if ((chunk == nullptr) || !chunk->IsValid())
1017  {
1018  // The chunk is not available, bail out
1019  return false;
1020  }
1021  chunk->GetBlockTypeMeta(a_RelPos, a_BlockType, a_BlockMeta);
1022  return true;
1023 }
1024 
1025 
1026 
1027 
1028 
1029 bool cChunk::UnboundedRelGetBlockType(Vector3i a_RelPos, BLOCKTYPE & a_BlockType) const
1030 {
1031  if (!cChunkDef::IsValidHeight(a_RelPos))
1032  {
1033  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1034  return false;
1035  }
1036  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1037  if ((chunk == nullptr) || !chunk->IsValid())
1038  {
1039  // The chunk is not available, bail out
1040  return false;
1041  }
1042  a_BlockType = chunk->GetBlock(a_RelPos);
1043  return true;
1044 }
1045 
1046 
1047 
1048 
1049 
1050 bool cChunk::UnboundedRelGetBlockMeta(Vector3i a_RelPos, NIBBLETYPE & a_BlockMeta) const
1051 {
1052  if (!cChunkDef::IsValidHeight(a_RelPos))
1053  {
1054  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1055  return false;
1056  }
1057  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1058  if ((chunk == nullptr) || !chunk->IsValid())
1059  {
1060  // The chunk is not available, bail out
1061  return false;
1062  }
1063  a_BlockMeta = chunk->GetMeta(a_RelPos);
1064  return true;
1065 }
1066 
1067 
1068 
1069 
1070 
1071 bool cChunk::UnboundedRelGetBlockBlockLight(Vector3i a_RelPos, NIBBLETYPE & a_BlockBlockLight) const
1072 {
1073  if (!cChunkDef::IsValidHeight(a_RelPos))
1074  {
1075  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1076  return false;
1077  }
1078  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1079  if ((chunk == nullptr) || !chunk->IsValid())
1080  {
1081  // The chunk is not available, bail out
1082  return false;
1083  }
1084  a_BlockBlockLight = chunk->GetBlockLight(a_RelPos);
1085  return true;
1086 }
1087 
1088 
1089 
1090 
1091 
1092 bool cChunk::UnboundedRelGetBlockSkyLight(Vector3i a_RelPos, NIBBLETYPE & a_BlockSkyLight) const
1093 {
1094  if (!cChunkDef::IsValidHeight(a_RelPos))
1095  {
1096  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1097  return false;
1098  }
1099  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1100  if ((chunk == nullptr) || !chunk->IsValid())
1101  {
1102  // The chunk is not available, bail out
1103  return false;
1104  }
1105  a_BlockSkyLight = chunk->GetSkyLight(a_RelPos);
1106  return true;
1107 }
1108 
1109 
1110 
1111 
1112 
1113 bool cChunk::UnboundedRelGetBlockLights(Vector3i a_RelPos, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const
1114 {
1115  if (!cChunkDef::IsValidHeight(a_RelPos))
1116  {
1117  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1118  return false;
1119  }
1120  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1121  if ((chunk == nullptr) || !chunk->IsValid())
1122  {
1123  // The chunk is not available, bail out
1124  return false;
1125  }
1126  a_BlockLight = chunk->GetBlockLight(a_RelPos);
1127  a_SkyLight = chunk->GetSkyLight (a_RelPos);
1128  return true;
1129 }
1130 
1131 
1132 
1133 
1134 
1135 bool cChunk::UnboundedRelSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
1136 {
1137  if (!cChunkDef::IsValidHeight(a_RelPos))
1138  {
1139  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1140  return false;
1141  }
1142  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1143  if ((chunk == nullptr) || !chunk->IsValid())
1144  {
1145  // The chunk is not available, bail out
1146  return false;
1147  }
1148  chunk->SetBlock(a_RelPos, a_BlockType, a_BlockMeta);
1149  return true;
1150 }
1151 
1152 
1153 
1154 
1155 
1156 bool cChunk::UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
1157 {
1158  if (!cChunkDef::IsValidHeight(a_RelPos))
1159  {
1160  LOGWARNING("%s: requesting a block with a_RelY out of range: %d", __FUNCTION__, a_RelPos.y);
1161  return false;
1162  }
1163  auto chunk = GetRelNeighborChunkAdjustCoords(a_RelPos);
1164  if ((chunk == nullptr) || !chunk->IsValid())
1165  {
1166  // The chunk is not available, bail out
1167  return false;
1168  }
1169  chunk->FastSetBlock(a_RelPos, a_BlockType, a_BlockMeta);
1170  return true;
1171 }
1172 
1173 
1174 
1175 
1176 
1177 int cChunk::GetHeight(int a_X, int a_Z) const
1178 {
1179  ASSERT((a_X >= 0) && (a_X < cChunkDef::Width) && (a_Z >= 0) && (a_Z < cChunkDef::Width));
1180  return m_HeightMap[a_X + a_Z * cChunkDef::Width];
1181 }
1182 
1183 
1184 
1185 
1186 
1187 bool cChunk::IsWeatherSunnyAt(int a_RelX, int a_RelZ) const
1188 {
1189  return m_World->IsWeatherSunny() || IsBiomeNoDownfall(GetBiomeAt(a_RelX, a_RelZ));
1190 }
1191 
1192 
1193 
1194 
1195 
1196 bool cChunk::IsWeatherWetAt(const int a_RelX, const int a_RelZ) const
1197 {
1198  const auto Biome = GetBiomeAt(a_RelX, a_RelZ);
1199  return m_World->IsWeatherWet() && !IsBiomeNoDownfall(Biome) && !IsBiomeCold(Biome);
1200 }
1201 
1202 
1203 
1204 
1205 
1206 bool cChunk::IsWeatherWetAt(const Vector3i a_Position) const
1207 {
1208  if ((a_Position.y < 0) || !IsWeatherWetAt(a_Position.x, a_Position.z))
1209  {
1210  return false;
1211  }
1212 
1213  if (a_Position.y >= cChunkDef::Height)
1214  {
1215  return true;
1216  }
1217 
1218  for (int y = GetHeight(a_Position.x, a_Position.z); y >= a_Position.y; y--)
1219  {
1220  if (cBlockInfo::IsRainBlocker(GetBlock({ a_Position.x, y, a_Position.z })))
1221  {
1222  return false;
1223  }
1224  }
1225 
1226  return true;
1227 }
1228 
1229 
1230 
1231 
1232 
1234 {
1235  auto * WaterSimulator = m_World->GetWaterSimulator();
1236  auto * LavaSimulator = m_World->GetLavaSimulator();
1237  auto * RedstoneSimulator = m_World->GetRedstoneSimulator();
1238 
1239  for (size_t SectionIdx = 0; SectionIdx != cChunkDef::NumSections; ++SectionIdx)
1240  {
1241  const auto * Section = m_BlockData.GetSection(SectionIdx);
1242  if (Section == nullptr)
1243  {
1244  continue;
1245  }
1246 
1247  for (size_t BlockIdx = 0; BlockIdx != ChunkBlockData::SectionBlockCount; ++BlockIdx)
1248  {
1249  const auto BlockType = (*Section)[BlockIdx];
1250  const auto Position = cChunkDef::IndexToCoordinate(BlockIdx + SectionIdx * ChunkBlockData::SectionBlockCount);
1251 
1252  RedstoneSimulator->AddBlock(*this, Position, BlockType);
1253  WaterSimulator->AddBlock(*this, Position, BlockType);
1254  LavaSimulator->AddBlock(*this, Position, BlockType);
1255  }
1256  }
1257 }
1258 
1259 
1260 
1261 
1262 
1263 void cChunk::SetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
1264 {
1265  FastSetBlock(a_RelPos, a_BlockType, a_BlockMeta);
1266 
1267  // Queue a check of this block's neighbors:
1268  m_BlocksToCheck.push(a_RelPos);
1269 
1270  // Wake up the simulators for this block:
1271  GetWorld()->GetSimulatorManager()->WakeUp(*this, a_RelPos);
1272 
1273  // If there was a block entity, remove it:
1274  if (const auto FindResult = m_BlockEntities.find(cChunkDef::MakeIndex(a_RelPos)); FindResult != m_BlockEntities.end())
1275  {
1276  auto & BlockEntity = *FindResult->second;
1277 
1278  BlockEntity.Destroy();
1279  BlockEntity.OnRemoveFromWorld();
1280 
1281  m_BlockEntities.erase(FindResult);
1283  }
1284 
1285  // If the new block is a block entity, create the entity object:
1286  if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
1287  {
1288  AddBlockEntity(cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, RelativeToAbsolute(a_RelPos), m_World));
1289  }
1290 }
1291 
1292 
1293 
1294 
1295 
1296 void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
1297 {
1298  ASSERT(cChunkDef::IsValidRelPos({ a_RelX, a_RelY, a_RelZ }));
1299  ASSERT(IsValid());
1300 
1301  const BLOCKTYPE OldBlockType = GetBlock(a_RelX, a_RelY, a_RelZ);
1302  const BLOCKTYPE OldBlockMeta = m_BlockData.GetMeta({ a_RelX, a_RelY, a_RelZ });
1303  if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
1304  {
1305  return;
1306  }
1307 
1308  bool ReplacingLiquids = (
1309  ((OldBlockType == E_BLOCK_STATIONARY_WATER) && (a_BlockType == E_BLOCK_WATER)) || // Replacing stationary water with water
1310  ((OldBlockType == E_BLOCK_WATER) && (a_BlockType == E_BLOCK_STATIONARY_WATER)) || // Replacing water with stationary water
1311  ((OldBlockType == E_BLOCK_STATIONARY_LAVA) && (a_BlockType == E_BLOCK_LAVA)) || // Replacing stationary lava with lava
1312  ((OldBlockType == E_BLOCK_LAVA) && (a_BlockType == E_BLOCK_STATIONARY_LAVA)) // Replacing lava with stationary lava
1313  );
1314 
1315  if (!ReplacingLiquids)
1316  {
1317  MarkDirty();
1318  }
1319 
1320  m_BlockData.SetBlock({ a_RelX, a_RelY, a_RelZ }, a_BlockType);
1321 
1322  // Queue block to be sent only if ...
1323  if (
1324  !( // ... the old and new blocktypes AREN'T leaves (because the client doesn't need meta updates)
1325  ((OldBlockType == E_BLOCK_LEAVES) && (a_BlockType == E_BLOCK_LEAVES)) ||
1326  ((OldBlockType == E_BLOCK_NEW_LEAVES) && (a_BlockType == E_BLOCK_NEW_LEAVES))
1327  ) && // ... AND ...
1328  (
1329  (OldBlockMeta != a_BlockMeta) || (!ReplacingLiquids)
1330  )
1331  )
1332  {
1333  m_PendingSendBlocks.emplace_back(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, a_BlockType, a_BlockMeta);
1334  }
1335 
1336  m_BlockData.SetMeta({ a_RelX, a_RelY, a_RelZ }, a_BlockMeta);
1337 
1338  // ONLY recalculate lighting if it's necessary!
1339  if (
1340  (cBlockInfo::GetLightValue (OldBlockType) != cBlockInfo::GetLightValue (a_BlockType)) ||
1341  (cBlockInfo::GetSpreadLightFalloff(OldBlockType) != cBlockInfo::GetSpreadLightFalloff(a_BlockType)) ||
1342  (cBlockInfo::IsTransparent (OldBlockType) != cBlockInfo::IsTransparent (a_BlockType))
1343  )
1344  {
1345  m_IsLightValid = false;
1346  }
1347 
1348  // Update heightmap, if needed:
1349  if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * cChunkDef::Width])
1350  {
1351  if (a_BlockType != E_BLOCK_AIR)
1352  {
1353  m_HeightMap[a_RelX + a_RelZ * cChunkDef::Width] = static_cast<HEIGHTTYPE>(a_RelY);
1354  }
1355  else
1356  {
1357  for (int y = a_RelY - 1; y > 0; --y)
1358  {
1359  if (GetBlock(a_RelX, y, a_RelZ) != E_BLOCK_AIR)
1360  {
1361  m_HeightMap[a_RelX + a_RelZ * cChunkDef::Width] = static_cast<HEIGHTTYPE>(y);
1362  break;
1363  }
1364  } // for y - column in m_BlockData
1365  }
1366  }
1367 }
1368 
1369 
1370 
1371 
1372 
1373 void cChunk::SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client)
1374 {
1375  const auto BlockEntity = GetBlockEntityRel({ a_RelX, a_RelY, a_RelZ });
1376 
1377  if (a_Client == nullptr)
1378  {
1379  // Queue the block (entity) for all clients in the chunk (will be sent in BroadcastPendingBlockChanges()):
1380  m_PendingSendBlocks.emplace_back(m_PosX, m_PosZ, a_RelX, a_RelY, a_RelZ, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
1381  if (BlockEntity != nullptr)
1382  {
1383  m_PendingSendBlockEntities.push_back(BlockEntity);
1384  }
1385  return;
1386  }
1387 
1388  const auto Position = PositionToWorldPosition(a_RelX, a_RelY, a_RelZ);
1389  a_Client->SendBlockChange(Position, GetBlock(a_RelX, a_RelY, a_RelZ), GetMeta(a_RelX, a_RelY, a_RelZ));
1390 
1391  // FS #268 - if a BlockEntity digging is cancelled by a plugin, the entire block entity must be re-sent to the client:
1392  if (BlockEntity != nullptr)
1393  {
1394  BlockEntity->SendTo(*a_Client);
1395  }
1396 }
1397 
1398 
1399 
1400 
1401 
1403 {
1404  const auto BlockEntityPtr = a_BlockEntity.get();
1405  [[maybe_unused]] const auto Result = m_BlockEntities.emplace(
1406  cChunkDef::MakeIndex(a_BlockEntity->GetRelX(), a_BlockEntity->GetPosY(), a_BlockEntity->GetRelZ()),
1407  std::move(a_BlockEntity)
1408  );
1409 
1410  ASSERT(Result.second); // No block entity already at this position.
1411  BlockEntityPtr->OnAddToWorld(*m_World, *this);
1412 }
1413 
1414 
1415 
1416 
1417 
1419 {
1420  const auto relPos = cChunkDef::AbsoluteToRelative(a_AbsPos);
1421 
1422  if (!cChunkDef::IsValidRelPos(relPos))
1423  {
1424  // Coordinates are outside outside this chunk, no block entities here
1425  return nullptr;
1426  }
1427 
1428  auto itr = m_BlockEntities.find(cChunkDef::MakeIndex(relPos));
1429  return (itr == m_BlockEntities.end()) ? nullptr : itr->second.get();
1430 }
1431 
1432 
1433 
1434 
1435 
1437 {
1438  ASSERT(cChunkDef::IsValidRelPos(a_RelPos));
1439  auto itr = m_BlockEntities.find(cChunkDef::MakeIndex(a_RelPos));
1440  return (itr == m_BlockEntities.end()) ? nullptr : itr->second.get();
1441 }
1442 
1443 
1444 
1445 
1446 
1447 bool cChunk::ShouldBeTicked(void) const
1448 {
1449  return IsValid() && (HasAnyClients() || (m_AlwaysTicked > 0));
1450 }
1451 
1452 
1453 
1454 
1455 
1456 void cChunk::SetAlwaysTicked(bool a_AlwaysTicked)
1457 {
1458  if (a_AlwaysTicked)
1459  {
1460  m_AlwaysTicked += 1;
1461  Stay(a_AlwaysTicked);
1462  }
1463  else
1464  {
1465  m_AlwaysTicked -= 1;
1466  Stay(a_AlwaysTicked);
1467  }
1468 }
1469 
1470 
1471 
1472 
1473 
1474 bool cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z)
1475 {
1476  cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z);
1477  if (be != nullptr)
1478  {
1479  return be->UsedBy(a_Player);
1480  }
1481  return false;
1482 }
1483 
1484 
1485 
1486 
1487 
1488 void cChunk::SetBiomeAt(int a_RelX, int a_RelZ, EMCSBiome a_Biome)
1489 {
1490  cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, a_Biome);
1491  MarkDirty();
1492 }
1493 
1494 
1495 
1496 
1497 
1498 void cChunk::SetAreaBiome(int a_MinRelX, int a_MaxRelX, int a_MinRelZ, int a_MaxRelZ, EMCSBiome a_Biome)
1499 {
1500  for (int z = a_MinRelZ; z <= a_MaxRelZ; z++)
1501  {
1502  for (int x = a_MinRelX; x <= a_MaxRelX; x++)
1503  {
1504  cChunkDef::SetBiome(m_BiomeMap, x, z, a_Biome);
1505  }
1506  }
1507  MarkDirty();
1508 
1509  // Re-send the chunk to all clients:
1510  for (auto ClientHandle : m_LoadedByClient)
1511  {
1513  } // for itr - m_LoadedByClient[]
1514 }
1515 
1516 
1517 
1518 
1519 
1521 {
1522  if (std::find(m_LoadedByClient.begin(), m_LoadedByClient.end(), a_Client) != m_LoadedByClient.end())
1523  {
1524  // Already there, nothing needed
1525  return false;
1526  }
1527 
1528  m_LoadedByClient.push_back(a_Client);
1529  return true;
1530 }
1531 
1532 
1533 
1534 
1535 
1537 {
1538  auto itr = std::remove(m_LoadedByClient.begin(), m_LoadedByClient.end(), a_Client);
1539  // We should always remove at most one client.
1540  ASSERT(std::distance(itr, m_LoadedByClient.end()) <= 1);
1541  // Note: itr can equal m_LoadedByClient.end()
1542  m_LoadedByClient.erase(itr, m_LoadedByClient.end());
1543 
1544  if (!a_Client->IsDestroyed())
1545  {
1546  for (auto & Entity : m_Entities)
1547  {
1548  /*
1549  // DEBUG:
1550  LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"",
1551  m_PosX, m_PosZ,
1552  (*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
1553  );
1554  */
1555  a_Client->SendDestroyEntity(*Entity);
1556  }
1557  }
1558 }
1559 
1560 
1561 
1562 
1563 
1565 {
1566  return std::find(m_LoadedByClient.begin(), m_LoadedByClient.end(), a_Client) != m_LoadedByClient.end();
1567 }
1568 
1569 
1570 
1571 
1572 
1573 bool cChunk::HasAnyClients(void) const
1574 {
1575  return !m_LoadedByClient.empty();
1576 }
1577 
1578 
1579 
1580 
1581 
1583 {
1584  if (!a_Entity->IsPlayer())
1585  {
1586  MarkDirty();
1587  }
1588 
1589  auto EntityPtr = a_Entity.get();
1590 
1591  ASSERT(std::find(m_Entities.begin(), m_Entities.end(), a_Entity) == m_Entities.end()); // Not there already
1592  m_Entities.emplace_back(std::move(a_Entity));
1593 
1594  ASSERT(EntityPtr->GetParentChunk() == nullptr);
1595  EntityPtr->SetParentChunk(this);
1596 }
1597 
1598 
1599 
1600 
1601 
1603 {
1604  ASSERT(a_Entity.GetParentChunk() == this);
1605  ASSERT(!a_Entity.IsTicking());
1606  a_Entity.SetParentChunk(nullptr);
1607 
1608  // Mark as dirty if it was a server-generated entity:
1609  if (!a_Entity.IsPlayer())
1610  {
1611  MarkDirty();
1612  }
1613 
1614  OwnedEntity Removed;
1615  m_Entities.erase(
1616  std::remove_if(
1617  m_Entities.begin(),
1618  m_Entities.end(),
1619  [&a_Entity, &Removed](decltype(m_Entities)::value_type & a_Value)
1620  {
1621  if (a_Value.get() == &a_Entity)
1622  {
1623  ASSERT(!Removed);
1624  Removed = std::move(a_Value);
1625  return true;
1626  }
1627 
1628  return false;
1629  }
1630  ),
1631  m_Entities.end()
1632  );
1633 
1634  return Removed;
1635 }
1636 
1637 
1638 
1639 
1640 
1641 bool cChunk::HasEntity(UInt32 a_EntityID) const
1642 {
1643  for (const auto & Entity : m_Entities)
1644  {
1645  if (Entity->GetUniqueID() == a_EntityID)
1646  {
1647  return true;
1648  }
1649  }
1650  return false;
1651 }
1652 
1653 
1654 
1655 
1656 
1658 {
1659  // The entity list is locked by the parent chunkmap's CS
1660  for (const auto & Entity : m_Entities)
1661  {
1662  if (Entity->IsTicking() && a_Callback(*Entity))
1663  {
1664  return false;
1665  }
1666  } // for itr - m_Entitites[]
1667  return true;
1668 }
1669 
1670 
1671 
1672 
1673 
1674 bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback a_Callback) const
1675 {
1676  // The entity list is locked by the parent chunkmap's CS
1677  for (const auto & Entity : m_Entities)
1678  {
1679  if (!Entity->IsTicking())
1680  {
1681  continue;
1682  }
1683  if (!Entity->GetBoundingBox().DoesIntersect(a_Box))
1684  {
1685  // The entity is not in the specified box
1686  continue;
1687  }
1688  if (a_Callback(*Entity))
1689  {
1690  return false;
1691  }
1692  } // for itr - m_Entitites[]
1693  return true;
1694 }
1695 
1696 
1697 
1698 
1699 
1700 bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback a_Callback, bool & a_CallbackResult) const
1701 {
1702  // The entity list is locked by the parent chunkmap's CS
1703  for (const auto & Entity : m_Entities)
1704  {
1705  if ((Entity->GetUniqueID() == a_EntityID) && (Entity->IsTicking()))
1706  {
1707  a_CallbackResult = a_Callback(*Entity);
1708  return true;
1709  }
1710  } // for itr - m_Entitites[]
1711  return false;
1712 }
1713 
1714 
1715 
1716 
1717 
1719 {
1720  // The blockentity list is locked by the parent chunkmap's CS
1721 
1722  for (auto & KeyPair : m_BlockEntities)
1723  {
1724  if (a_Callback(*KeyPair.second))
1725  {
1726  return false;
1727  }
1728  }
1729 
1730  return true;
1731 }
1732 
1733 
1734 
1735 
1736 
1738 {
1739  // The blockentity list is locked by the parent chunkmap's CS
1740 
1741  const auto BlockEntity = GetBlockEntityRel(a_Position);
1742  if (BlockEntity == nullptr)
1743  {
1744  return false; // No block entity here
1745  }
1746 
1747  const bool Result = a_Callback(*BlockEntity);
1748  m_PendingSendBlockEntities.push_back(BlockEntity);
1749  MarkDirty();
1750  return Result;
1751 }
1752 
1753 
1754 
1755 
1756 
1757 void cChunk::GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
1758 {
1759  a_BlockType = GetBlock(a_RelPos);
1760  a_BlockMeta = GetMeta(a_RelPos);
1761 }
1762 
1763 
1764 
1765 
1766 
1767 void cChunk::GetBlockInfo(Vector3i a_RelPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) const
1768 {
1769  a_BlockType = GetBlock(a_RelPos);
1770  a_Meta = m_BlockData.GetMeta(a_RelPos);
1771  a_SkyLight = m_LightData.GetSkyLight(a_RelPos);
1772  a_BlockLight = m_LightData.GetBlockLight(a_RelPos);
1773 }
1774 
1775 
1776 
1777 
1778 
1779 bool cChunk::GetChunkAndRelByAbsolute(const Vector3d & a_Position, cChunk ** a_Chunk, Vector3i & a_Rel)
1780 {
1781  return GetChunkAndRelByAbsolute(Vector3i(FloorC(a_Position.x), FloorC(a_Position.y), FloorC(a_Position.z)), a_Chunk, a_Rel);
1782 }
1783 
1784 
1785 
1786 
1787 
1788 bool cChunk::GetChunkAndRelByAbsolute(const Vector3i & a_Position, cChunk ** a_Chunk, Vector3i & a_Rel)
1789 {
1790  *a_Chunk = this->GetNeighborChunk(a_Position.x, a_Position.z);
1791  if ((*a_Chunk == nullptr) || !(*a_Chunk)->IsValid())
1792  {
1793  return false;
1794  }
1795 
1796  a_Rel.x = a_Position.x - (*a_Chunk)->GetPosX() * cChunkDef::Width;
1797  a_Rel.y = a_Position.y;
1798  a_Rel.z = a_Position.z - (*a_Chunk)->GetPosZ() * cChunkDef::Width;
1799  return true;
1800 }
1801 
1802 
1803 
1804 
1805 
1806 cChunk * cChunk::GetNeighborChunk(int a_BlockX, int a_BlockZ)
1807 {
1808  // Convert coords to relative, then call the relative version:
1809  a_BlockX -= m_PosX * cChunkDef::Width;
1810  a_BlockZ -= m_PosZ * cChunkDef::Width;
1811  return GetRelNeighborChunk(a_BlockX, a_BlockZ);
1812 }
1813 
1814 
1815 
1816 
1817 
1818 cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
1819 {
1820  // If the relative coords are too far away, use the parent's chunk lookup instead:
1821  if ((a_RelX < -128) || (a_RelX > 128) || (a_RelZ < -128) || (a_RelZ > 128))
1822  {
1823  int BlockX = m_PosX * cChunkDef::Width + a_RelX;
1824  int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ;
1825  int ChunkX, ChunkZ;
1826  cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
1827  return m_ChunkMap->FindChunk(ChunkX, ChunkZ);
1828  }
1829 
1830  // Walk the neighbors:
1831  bool ReturnThis = true;
1832  if (a_RelX < 0)
1833  {
1834  if (m_NeighborXM != nullptr)
1835  {
1836  cChunk * Candidate = m_NeighborXM->GetRelNeighborChunk(a_RelX + cChunkDef::Width, a_RelZ);
1837  if (Candidate != nullptr)
1838  {
1839  return Candidate;
1840  }
1841  }
1842  // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
1843  ReturnThis = false;
1844  }
1845  else if (a_RelX >= cChunkDef::Width)
1846  {
1847  if (m_NeighborXP != nullptr)
1848  {
1849  cChunk * Candidate = m_NeighborXP->GetRelNeighborChunk(a_RelX - cChunkDef::Width, a_RelZ);
1850  if (Candidate != nullptr)
1851  {
1852  return Candidate;
1853  }
1854  }
1855  // Going X first failed, but if the request is crossing Z as well, let's try the Z first later on.
1856  ReturnThis = false;
1857  }
1858 
1859  if (a_RelZ < 0)
1860  {
1861  if (m_NeighborZM != nullptr)
1862  {
1863  return m_NeighborZM->GetRelNeighborChunk(a_RelX, a_RelZ + cChunkDef::Width);
1864  // For requests crossing both X and Z, the X-first way has been already tried
1865  }
1866  return nullptr;
1867  }
1868  else if (a_RelZ >= cChunkDef::Width)
1869  {
1870  if (m_NeighborZP != nullptr)
1871  {
1872  return m_NeighborZP->GetRelNeighborChunk(a_RelX, a_RelZ - cChunkDef::Width);
1873  // For requests crossing both X and Z, the X-first way has been already tried
1874  }
1875  return nullptr;
1876  }
1877 
1878  return (ReturnThis ? this : nullptr);
1879 }
1880 
1881 
1882 
1883 
1884 
1886 {
1887  cChunk * ToReturn = const_cast<cChunk *>(this);
1888 
1889  // The most common case: inside this chunk:
1890  if (
1891  (a_RelPos.x >= 0) && (a_RelPos.x < cChunkDef::Width) &&
1892  (a_RelPos.z >= 0) && (a_RelPos.z < cChunkDef::Width)
1893  )
1894  {
1895  return ToReturn;
1896  }
1897 
1898  // Request for a different chunk, calculate chunk offset:
1899  int RelX = a_RelPos.x; // Make a local copy of the coords (faster access)
1900  int RelZ = a_RelPos.z;
1901  while ((RelX >= cChunkDef::Width) && (ToReturn != nullptr))
1902  {
1903  RelX -= cChunkDef::Width;
1904  ToReturn = ToReturn->m_NeighborXP;
1905  }
1906  while ((RelX < 0) && (ToReturn != nullptr))
1907  {
1908  RelX += cChunkDef::Width;
1909  ToReturn = ToReturn->m_NeighborXM;
1910  }
1911  while ((RelZ >= cChunkDef::Width) && (ToReturn != nullptr))
1912  {
1913  RelZ -= cChunkDef::Width;
1914  ToReturn = ToReturn->m_NeighborZP;
1915  }
1916  while ((RelZ < 0) && (ToReturn != nullptr))
1917  {
1918  RelZ += cChunkDef::Width;
1919  ToReturn = ToReturn->m_NeighborZM;
1920  }
1921  if (ToReturn != nullptr)
1922  {
1923  a_RelPos.x = RelX;
1924  a_RelPos.z = RelZ;
1925  return ToReturn;
1926  }
1927 
1928  // The chunk cannot be walked through neighbors, find it through the chunkmap:
1929  int AbsX = a_RelPos.x + m_PosX * cChunkDef::Width;
1930  int AbsZ = a_RelPos.z + m_PosZ * cChunkDef::Width;
1931  int DstChunkX, DstChunkZ;
1932  cChunkDef::BlockToChunk(AbsX, AbsZ, DstChunkX, DstChunkZ);
1933  ToReturn = m_ChunkMap->FindChunk(DstChunkX, DstChunkZ);
1934  a_RelPos.x = AbsX - DstChunkX * cChunkDef::Width;
1935  a_RelPos.z = AbsZ - DstChunkZ * cChunkDef::Width;
1936  return ToReturn;
1937 }
1938 
1939 
1940 
1941 
1942 
1943 void cChunk::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client)
1944 {
1945  cBlockEntity * Entity = GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ);
1946  if (Entity == nullptr)
1947  {
1948  return;
1949  }
1950  Entity->SendTo(a_Client);
1951 }
1952 
1953 
1954 
1955 
1956 
1957 void cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ, int & a_BlockX, int & a_BlockY, int & a_BlockZ)
1958 {
1959  a_BlockY = a_RelY;
1960  a_BlockX = m_PosX * cChunkDef::Width + a_RelX;
1961  a_BlockZ = m_PosZ * cChunkDef::Width + a_RelZ;
1962 }
1963 
1964 
1965 
1966 
1967 
1968 Vector3i cChunk::PositionToWorldPosition(int a_RelX, int a_RelY, int a_RelZ)
1969 {
1970  return Vector3i(m_PosX * cChunkDef::Width + a_RelX, a_RelY, m_PosZ * cChunkDef::Width + a_RelZ);
1971 }
1972 
1973 
1974 
1975 
1976 
1978 {
1979  a_Skylight -= m_World->GetSkyDarkness();
1980  // Because NIBBLETYPE is unsigned, we clamp it to 0 .. 15 by checking for values above 15
1981  return (a_Skylight < 16)? a_Skylight : 0;
1982 }
1983 
1984 
1985 
1986 
1987 
1989 {
1990  return m_World->IsSlimeChunk(m_PosX, m_PosZ);
1991 }
int GetSnowStartHeight(EMCSBiome a_Biome)
Returns the height when a biome when a biome starts snowing.
Definition: BiomeDef.cpp:275
bool IsBiomeNoDownfall(EMCSBiome a_Biome)
Returns true if the biome has no downfall - deserts and savannas.
Definition: BiomeDef.cpp:142
bool IsBiomeCold(EMCSBiome a_Biome)
Returns true if the biome is cold (has snow and snowfall at higher elevations but not at regular heig...
Definition: BiomeDef.cpp:196
EMCSBiome
Biome IDs The first batch corresponds to the clientside biomes, used by MineCraft.
Definition: BiomeDef.h:18
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:10
@ E_BLOCK_NEW_LEAVES
Definition: BlockType.h:180
@ E_BLOCK_WATER
Definition: BlockType.h:18
@ E_BLOCK_STATIONARY_LAVA
Definition: BlockType.h:21
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_LEAVES
Definition: BlockType.h:28
@ E_BLOCK_ICE
Definition: BlockType.h:93
@ E_BLOCK_YELLOW_FLOWER
Definition: BlockType.h:283
@ E_BLOCK_SNOW
Definition: BlockType.h:92
@ E_BLOCK_BROWN_MUSHROOM
Definition: BlockType.h:49
@ E_BLOCK_RED_ROSE
Definition: BlockType.h:284
@ E_BLOCK_RED_MUSHROOM
Definition: BlockType.h:50
@ E_BLOCK_STATIONARY_WATER
Definition: BlockType.h:19
@ E_BLOCK_LAVA
Definition: BlockType.h:20
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
unsigned char HEIGHTTYPE
The type used by the heightmap.
Definition: ChunkDef.h:47
std::unique_ptr< cEntity > OwnedEntity
Definition: ChunkDef.h:32
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
@ eWeather_ThunderStorm
Definition: Defines.h:163
@ eWeather_Rain
Definition: Defines.h:162
MTRand & GetRandomProvider()
Returns the current thread's random number source.
Definition: FastRandom.cpp:12
unsigned int UInt32
Definition: Globals.h:157
#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
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
BlockType
Definition: BlockTypes.h:4
Vector3< int > Vector3i
Definition: Vector3.h:487
bool CallHookBlockToPickups(cWorld &a_World, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cBlockEntity *a_BlockEntity, const cEntity *a_Digger, const cItem *a_Tool, cItems &a_Pickups)
size_t MakeIndex(Vector3i a_RelPos) const
Returns the index into the internal arrays for the specified coords.
Definition: BlockArea.h:397
NIBBLETYPE * GetBlockMetas(void) const
Definition: BlockArea.h:390
@ baBlockEntities
Definition: BlockArea.h:53
int GetSizeY(void) const
Definition: BlockArea.h:348
BLOCKTYPE * GetBlockTypes(void) const
Returns the internal pointer to the block types.
Definition: BlockArea.h:389
int GetSizeZ(void) const
Definition: BlockArea.h:349
int GetSizeX(void) const
Definition: BlockArea.h:347
const cBlockEntities & GetBlockEntities(void) const
Direct read-only access to block entities.
Definition: BlockArea.h:426
virtual void SendTo(cClientHandle &a_Client)=0
Sends the packet defining the block entity to the client specified.
static OwnedBlockEntity CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld *a_World=nullptr)
Creates a new block entity for the specified block type at the specified absolute pos.
Definition: BlockEntity.cpp:77
virtual bool UsedBy(cPlayer *a_Player)=0
Called when a player uses this entity; should open the UI window.
static bool IsBlockEntityBlockType(BLOCKTYPE a_BlockType)
Returns true if the specified blocktype is supposed to have an associated block entity.
static NIBBLETYPE GetSpreadLightFalloff(BLOCKTYPE Block)
How much light do the blocks consume?
Definition: BlockInfo.cpp:447
static bool IsSnowable(BLOCKTYPE Block)
Definition: BlockInfo.cpp:879
static NIBBLETYPE GetLightValue(BLOCKTYPE Block)
How much light do the blocks emit on their own?
Definition: BlockInfo.cpp:411
static bool IsTransparent(BLOCKTYPE Block)
Is a block transparent? (https://minecraft.wiki/w/Opacity)
Definition: BlockInfo.cpp:961
static bool IsRainBlocker(BLOCKTYPE Block)
Does this block block the passage of rain?
Definition: BlockInfo.cpp:847
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, const cItem *a_Tool=nullptr) const
Returns the pickups that would result if the block was mined by a_Digger using a_Tool.
void Check(cChunkInterface &ChunkInterface, cBlockPluginInterface &a_PluginInterface, Vector3i a_RelPos, cChunk &a_Chunk) const
Called when one of the neighbors gets set; equivalent to MC block update.
virtual int Grow(cChunk &a_Chunk, Vector3i a_RelPos, int a_NumStages=1) const
Grows this block, if it supports growing, by the specified amount of stages (at most).
Definition: BlockHandler.h:179
static const cBlockHandler & For(BLOCKTYPE a_BlockType)
virtual void OnUpdate(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cBlockPluginInterface &a_BlockPluginInterface, cChunk &a_Chunk, const Vector3i a_RelPos) const
Called when the block gets ticked either by a random tick or by a queued tick.
Represents two sets of coords, minimum and maximum for each direction.
Definition: BoundingBox.h:24
Definition: Chunk.h:36
bool GetChunkAndRelByAbsolute(const Vector3d &a_Position, cChunk **a_Chunk, Vector3i &a_Rel)
Convert absolute coordinates into relative coordinates.
Definition: Chunk.cpp:1779
void AddBlockEntity(OwnedBlockEntity a_BlockEntity)
Takes ownership of a block entity, which MUST actually reside in this chunk.
Definition: Chunk.cpp:1402
NIBBLETYPE GetBlockLight(Vector3i a_RelPos) const
Get the level of artificial light illuminating the block (0 - 15)
Definition: Chunk.h:302
void GetAllData(cChunkDataCallback &a_Callback) const
Gets all chunk data, calls the a_Callback's methods for each data type.
Definition: Chunk.cpp:307
ePresence m_Presence
Holds the presence status of the chunk - if it is present, or in the loader / generator queue,...
Definition: Chunk.h:480
void TickBlocks(void)
Ticks several random blocks in the chunk.
Definition: Chunk.cpp:834
std::vector< OwnedEntity > m_Entities
Definition: Chunk.h:502
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:279
bool ShouldBeTicked(void) const
Returns true if the chunk should be ticked in the tick-thread.
Definition: Chunk.cpp:1447
cChunk * m_NeighborXM
Definition: Chunk.h:522
std::vector< cBlockEntity * > m_PendingSendBlockEntities
Block entities that have been touched and need to be sent to all clients.
Definition: Chunk.h:494
void CheckBlocks()
Checks the block scheduled for checking in m_ToTickBlocks[].
Definition: Chunk.cpp:812
EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const
Definition: Chunk.h:191
void SpawnMobs(cMobSpawner &a_MobSpawner)
Try to Spawn Monsters inside chunk.
Definition: Chunk.cpp:611
void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle &a_Client)
Definition: Chunk.cpp:1943
unsigned m_AlwaysTicked
If greater than zero, the chunk is ticked even if it has no clients.
Definition: Chunk.h:537
bool IsSlimeChunk() const
Returns true if slimes should spawn in the chunk.
Definition: Chunk.cpp:1988
bool UnboundedRelGetBlockMeta(Vector3i a_RelPos, NIBBLETYPE &a_BlockMeta) const
Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap...
Definition: Chunk.cpp:1050
int GrowPlantAt(Vector3i a_RelPos, int a_NumStages=1)
Grows the plant at the specified position by at most a_NumStages.
Definition: Chunk.cpp:999
bool UseBlockEntity(cPlayer *a_Player, int a_X, int a_Y, int a_Z)
Use block entity on coordinate.
Definition: Chunk.cpp:1474
void SetAllData(SetChunkData &&a_SetChunkData)
Sets all chunk data as either loaded from the storage or generated.
Definition: Chunk.cpp:331
bool DoWithBlockEntityAt(Vector3i a_Position, cBlockEntityCallback a_Callback)
Calls the callback for the block entity at the specified coords; returns false if there's no block en...
Definition: Chunk.cpp:1737
void SetLight(const cChunkDef::BlockNibbles &a_BlockLight, const cChunkDef::BlockNibbles &a_SkyLight)
Definition: Chunk.cpp:402
bool ForEachEntity(cEntityCallback a_Callback) const
Calls the callback for each entity; returns true if all entities processed, false if the callback abo...
Definition: Chunk.cpp:1657
ChunkLightData m_LightData
Definition: Chunk.h:513
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
Definition: Chunk.cpp:1296
void MarkRegenerating(void)
Marks all clients attached to this chunk as wanting this chunk.
Definition: Chunk.cpp:176
void WriteBlockArea(cBlockArea &a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
Writes the specified cBlockArea at the coords specified.
Definition: Chunk.cpp:420
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:146
void AddEntity(OwnedEntity a_Entity)
Definition: Chunk.cpp:1582
Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition) const
Converts the coord relative to this chunk into an absolute coord.
Definition: Chunk.h:450
void GetThreeRandomNumbers(int &a_X, int &a_Y, int &a_Z, int a_MaxX, int a_MaxY, int a_MaxZ)
Definition: Chunk.cpp:577
void MarkLoaded(void)
Definition: Chunk.cpp:282
cChunk * GetRelNeighborChunkAdjustCoords(Vector3i &a_RelPos) const
Returns the chunk into which the relatively-specified block belongs.
Definition: Chunk.cpp:1885
cChunkCoords GetPos() const
Definition: Chunk.h:133
bool HasClient(cClientHandle *a_Client)
Returns true if the specified client is present in this chunk.
Definition: Chunk.cpp:1564
int GetHeight(int a_X, int a_Z) const
Definition: Chunk.cpp:1177
int m_PosX
Definition: Chunk.h:508
void WakeUpSimulators(void)
Wakes up each simulator for its specific blocks; through all the blocks in the chunk.
Definition: Chunk.cpp:1233
cChunk * m_NeighborXP
Definition: Chunk.h:523
void MarkDirty(void)
Definition: Chunk.h:265
void GetRandomBlockCoords(int &a_X, int &a_Y, int &a_Z)
Definition: Chunk.cpp:598
bool IsWeatherSunnyAt(int a_RelX, int a_RelZ) const
Returns true if it is sunny at the specified location.
Definition: Chunk.cpp:1187
NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const
Light alterations based on time.
Definition: Chunk.cpp:1977
bool m_IsDirty
Definition: Chunk.h:483
void SetBlock(Vector3i a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Definition: Chunk.cpp:1263
bool ForEachEntityInBox(const cBoundingBox &a_Box, cEntityCallback a_Callback) const
Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
Definition: Chunk.cpp:1674
bool CanUnload(void) const
Definition: Chunk.cpp:207
cBlockEntity * GetBlockEntity(Vector3i a_AbsPos)
Returns the block entity at the specified (absolute) coords.
Definition: Chunk.cpp:1418
void SetPresence(ePresence a_Presence)
Sets the chunk's presence.
Definition: Chunk.cpp:163
void SetBiomeAt(int a_RelX, int a_RelZ, EMCSBiome a_Biome)
Sets the biome at the specified relative coords.
Definition: Chunk.cpp:1488
bool UnboundedRelSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in ...
Definition: Chunk.cpp:1135
void MarkLoadFailed(void)
Queues the chunk for generating.
Definition: Chunk.cpp:292
void MarkSaved(void)
Definition: Chunk.cpp:269
bool AddClient(cClientHandle *a_Client)
Adds a client to the chunk; returns true if added, false if already there.
Definition: Chunk.cpp:1520
bool UnboundedRelGetBlockSkyLight(Vector3i a_RelPos, NIBBLETYPE &a_SkyLight) const
Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_Chun...
Definition: Chunk.cpp:1092
cChunk * GetRelNeighborChunk(int a_RelX, int a_RelZ)
Returns the chunk into which the relatively-specified block belongs, by walking the neighbors.
Definition: Chunk.cpp:1818
void ApplyWeatherToTop(void)
Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes.
Definition: Chunk.cpp:871
bool UnboundedRelFastSetBlock(Vector3i a_RelPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap...
Definition: Chunk.cpp:1156
cFluidSimulatorData * m_WaterSimulatorData
Definition: Chunk.h:529
ePresence
Represents the presence state of the chunk.
Definition: Chunk.h:41
@ cpQueued
The chunk is not present, but is queued for loading / generation.
Definition: Chunk.h:43
@ cpPresent
The chunk is present.
Definition: Chunk.h:44
void Stay(bool a_Stay=true)
Sets or resets the internal flag that prevents chunk from being unloaded.
Definition: Chunk.cpp:529
void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle *a_Client)
Definition: Chunk.cpp:1373
cItems PickupsFromBlock(Vector3i a_RelPos, const cEntity *a_Digger, const cItem *a_Tool)
Returns the pickups that would be produced, if the specified block was dug up by a_Digger using a_Too...
Definition: Chunk.cpp:964
bool CanUnloadAfterSaving(void) const
Returns true if the chunk could have been unloaded if it weren't dirty.
Definition: Chunk.cpp:221
cChunk * GetNeighborChunk(int a_BlockX, int a_BlockZ)
Returns the chunk into which the specified block belongs, by walking the neighbors.
Definition: Chunk.cpp:1806
cChunkDef::HeightMap m_HeightMap
Definition: Chunk.h:515
void TickBlock(const Vector3i a_RelPos)
Ticks a single block.
Definition: Chunk.cpp:757
int m_PosZ
Definition: Chunk.h:508
void MarkSaving(void)
Definition: Chunk.cpp:260
bool UnboundedRelGetBlock(Vector3i a_RelCoords, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in ...
Definition: Chunk.cpp:1008
ChunkBlockData m_BlockData
Definition: Chunk.h:512
cFluidSimulatorData * m_LavaSimulatorData
Definition: Chunk.h:530
bool IsWeatherWetAt(int a_RelX, int a_RelZ) const
Returns true if it is raining or storming at the specified location, taking into account biomes.
Definition: Chunk.cpp:1196
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:58
bool UnboundedRelGetBlockBlockLight(Vector3i a_RelPos, NIBBLETYPE &a_BlockLight) const
Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_Ch...
Definition: Chunk.cpp:1071
cWorld * GetWorld(void) const
Definition: Chunk.h:135
cChunk * m_NeighborZP
Definition: Chunk.h:525
cBlockEntity * GetBlockEntityRel(Vector3i a_RelPos)
Returns the block entity at the specified (relative) coords.
Definition: Chunk.cpp:1436
void GetBlockInfo(Vector3i a_RelPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_Meta, NIBBLETYPE &a_SkyLight, NIBBLETYPE &a_BlockLight) const
Definition: Chunk.cpp:1767
std::vector< cClientHandle * > m_LoadedByClient
Definition: Chunk.h:501
void CollectMobCensus(cMobCensus &toFill)
Recence all mobs proximities to players in order to know what to do with them.
Definition: Chunk.cpp:546
std::queue< Vector3i > m_BlocksToCheck
A queue of relative positions to call cBlockHandler::Check on.
Definition: Chunk.h:498
cBlockEntities m_BlockEntities
Definition: Chunk.h:503
cWorld * m_World
Definition: Chunk.h:509
void MoveEntityToNewChunk(OwnedEntity a_Entity)
Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends s...
Definition: Chunk.cpp:768
cChunk(int a_ChunkX, int a_ChunkZ, cChunkMap *a_ChunkMap, cWorld *a_World)
Definition: Chunk.cpp:41
void OnUnload()
Called when the chunkmap unloads unused chunks.
Definition: Chunk.cpp:235
bool ForEachBlockEntity(cBlockEntityCallback a_Callback)
Calls the callback for each block entity; returns true if all block entities processed,...
Definition: Chunk.cpp:1718
bool UnboundedRelGetBlockLights(Vector3i a_RelPos, NIBBLETYPE &a_BlockLight, NIBBLETYPE &a_SkyLight) const
Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or ...
Definition: Chunk.cpp:1113
unsigned m_StayCount
Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero,...
Definition: Chunk.h:506
Vector3i m_BlockToTick
Relative coords of the block to tick first in the next Tick() call.
Definition: Chunk.h:520
void BroadcastPendingChanges(void)
Flushes the pending block (entity) queue, and clients' outgoing data buffers.
Definition: Chunk.cpp:120
~cChunk()
Definition: Chunk.cpp:86
void Tick(std::chrono::milliseconds a_Dt)
Definition: Chunk.cpp:685
cChunkDef::BiomeMap m_BiomeMap
Definition: Chunk.h:516
sSetBlockVector m_PendingSendBlocks
Blocks that have changed and need to be sent to all clients.
Definition: Chunk.h:489
bool m_IsLightValid
Definition: Chunk.h:482
cRedstoneSimulatorChunkData * m_RedstoneSimulatorData
Definition: Chunk.h:532
OwnedEntity RemoveEntity(cEntity &a_Entity)
Releases ownership of the given entity if it was found in this chunk.
Definition: Chunk.cpp:1602
cChunk * m_NeighborZM
Definition: Chunk.h:524
bool m_IsSaving
Definition: Chunk.h:484
void SetAlwaysTicked(bool a_AlwaysTicked)
Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter.
Definition: Chunk.cpp:1456
cChunkMap * m_ChunkMap
Definition: Chunk.h:510
bool UnboundedRelGetBlockType(Vector3i a_RelCoords, BLOCKTYPE &a_BlockType) const
Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap...
Definition: Chunk.cpp:1029
void SetAreaBiome(int a_MinRelX, int a_MaxRelX, int a_MinRelZ, int a_MaxRelZ, EMCSBiome a_Biome)
Sets the biome in the specified relative coords area.
Definition: Chunk.cpp:1498
bool HasAnyClients(void) const
Returns true if theres any client in the chunk; false otherwise.
Definition: Chunk.cpp:1573
bool HasEntity(UInt32 a_EntityID) const
Definition: Chunk.cpp:1641
Vector3i PositionToWorldPosition(Vector3i a_RelPos)
Definition: Chunk.h:257
bool HasPlayerEntities() const
Check m_Entities for cPlayer objects.
Definition: Chunk.cpp:192
void GetBlockTypeMeta(Vector3i a_RelPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Definition: Chunk.cpp:1757
bool DoWithEntityByID(UInt32 a_EntityID, cEntityCallback a_Callback, bool &a_CallbackResult) const
Calls the callback if the entity with the specified ID is found, with the entity object as the callba...
Definition: Chunk.cpp:1700
void RemoveClient(cClientHandle *a_Client)
Removes the specified client from the chunk; ignored if client not in chunk.
Definition: Chunk.cpp:1536
static constexpr size_t SectionBlockCount
Definition: ChunkData.h:59
NIBBLETYPE GetMeta(Vector3i a_Position) const
Definition: ChunkData.h:81
BlockArray * GetSection(size_t a_Y) const
Definition: ChunkData.h:83
void SetMeta(Vector3i a_Position, NIBBLETYPE a_Meta)
Definition: ChunkData.h:87
void SetBlock(Vector3i a_Position, BLOCKTYPE a_Block)
Definition: ChunkData.h:86
void SetAll(const cChunkDef::BlockNibbles &a_BlockLightSource, const cChunkDef::BlockNibbles &a_SkyLightSource)
Definition: ChunkData.cpp:212
NIBBLETYPE GetSkyLight(Vector3i a_Position) const
Definition: ChunkData.h:120
NIBBLETYPE GetBlockLight(Vector3i a_Position) const
Definition: ChunkData.h:119
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
static Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition, cChunkCoords a_ChunkCoords)
Converts relative block coordinates into absolute coordinates with a known chunk location.
Definition: ChunkDef.h:174
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static void AbsoluteToRelative(int &a_X, int &a_Y, int &a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords into relative (chunk + block) coords:
Definition: ChunkDef.h:147
static bool IsValidRelPos(Vector3i a_RelPos)
Validates a chunk relative coordinate.
Definition: ChunkDef.h:199
static size_t MakeIndex(int x, int y, int z)
Definition: ChunkDef.h:227
static const int Width
Definition: ChunkDef.h:124
NIBBLETYPE BlockNibbles[NumBlocks/2]
The type used for block data in nibble format, AXIS_ORDER ordering.
Definition: ChunkDef.h:143
static const size_t NumSections
Definition: ChunkDef.h:129
static const int Height
Definition: ChunkDef.h:125
static Vector3i IndexToCoordinate(size_t index)
Definition: ChunkDef.h:246
static const int NumBlocks
Definition: ChunkDef.h:126
static void SetBiome(BiomeMap &a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
Definition: ChunkDef.h:327
Interface class used for comparing clients of two chunks.
Definition: ChunkDef.h:370
void QueueGenerateChunk(cChunkCoords a_Coords, bool a_ForceRegeneration, cChunkCoordCallback *a_Callback=nullptr)
Queues the chunk for generation If a-ForceGenerate is set, the chunk is regenerated even if the data ...
void ChunkValidated(void)
Definition: ChunkMap.cpp:1458
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ)
Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSChunks is locked.
Definition: ChunkMap.cpp:69
cChunk & ConstructChunk(int a_ChunkX, int a_ChunkZ)
Returns or creates and returns a chunk pointer corresponding to the given chunk coordinates.
Definition: ChunkMap.cpp:39
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback &a_Callback)
Compares clients of two chunks, calls the callback accordingly.
Definition: ChunkMap.cpp:822
void SendDestroyEntity(const cEntity &a_Entity)
void SendBlockChange(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool IsDestroyed(void) const
Definition: ClientHandle.h:148
Definition: Cuboid.h:10
Definition: Entity.h:76
bool IsPlayer(void) const
Definition: Entity.h:160
cChunk * GetParentChunk()
Returns the chunk responsible for ticking this entity.
Definition: Entity.h:541
bool IsTicking(void) const
Returns true if the entity is valid and ticking.
Definition: Entity.cpp:2259
void SetPosition(double a_PosX, double a_PosY, double a_PosZ)
Definition: Entity.h:218
void SetParentChunk(cChunk *a_Chunk)
Sets the parent chunk, which is the chunk responsible for ticking this entity.
Definition: Entity.cpp:234
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:297
Definition: Player.h:29
Definition: Item.h:37
const cItemHandler & GetHandler(void) const
Returns the cItemHandler responsible for this item type.
Definition: Item.cpp:216
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:215
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) const
Returns whether this tool / item can harvest a specific block (e.g.
This class is used to collect information, for each Mob, what is the distance of the closest player i...
Definition: MobCensus.h:26
void CollectSpawnableChunk(cChunk &a_Chunk)
Definition: MobCensus.cpp:53
void CollectMob(cMonster &a_Monster, cChunk &a_Chunk, double a_Distance)
Collect a mob - its distance to player, its family ...
Definition: MobCensus.cpp:10
This class is used to determine which monster can be spawned in which place it is essentially static ...
Definition: MobSpawner.h:16
bool CheckPackCenter(BLOCKTYPE a_BlockType)
Check if specified block can be a Pack center for this spawner.
Definition: MobSpawner.cpp:31
void NewPack(void)
Mark the beginning of a new Pack.
Definition: MobSpawner.cpp:539
cMonster * TryToSpawnHere(cChunk *a_Chunk, Vector3i a_RelPos, EMCSBiome a_Biome, int &a_MaxPackSize)
Try to create a monster here If this is the first of a Pack, determine the type of monster a_Biome,...
Definition: MobSpawner.cpp:480
static cRoot * Get()
Definition: Root.h:52
cPluginManager * GetPluginManager(void)
Definition: Root.h:111
Contains the data for a loaded / generated chunk, ready to be set into a cWorld.
Definition: SetChunkData.h:12
void SimulateChunk(std::chrono::milliseconds a_DT, int a_ChunkX, int a_ChunkZ, cChunk *a_Chunk)
Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct acce...
void WakeUp(cChunk &a_Chunk, Vector3i a_Position)
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Definition: World.h:53
bool IsSlimeChunk(int a_ChunkX, int a_ChunkZ) const
Returns true if slimes should spawn in the chunk.
Definition: World.cpp:3235
bool IsWeatherSunny(void) const
Returns true if the current weather is sunny.
Definition: World.h:811
cChunkGeneratorThread & GetGenerator(void)
Definition: World.h:850
cFluidSimulator * GetWaterSimulator(void)
Definition: World.h:599
int GetTickRandomNumber(int a_Range)
Returns a random number in range [0 .
Definition: World.cpp:2978
bool IsDeepSnowEnabled(void) const
Definition: World.h:127
void ForceSendChunkTo(int a_ChunkX, int a_ChunkZ, cChunkSender::Priority a_Priority, cClientHandle *a_Client)
Sends the chunk to the client specified, even if the client already has the chunk.
Definition: World.cpp:2532
eWeather GetWeather(void) const
Returns the current weather.
Definition: World.h:808
NIBBLETYPE GetSkyDarkness()
Get the current darkness level based on the time.
Definition: World.h:886
cRedstoneSimulator * GetRedstoneSimulator(void)
Definition: World.h:601
cFluidSimulator * GetLavaSimulator(void)
Definition: World.h:600
cChunkMap * GetChunkMap(void)
Definition: World.h:852
bool IsWeatherWet(void) const
Returns true if the world currently has any precipitation - rain, storm or snow.
Definition: World.h:835
cSimulatorManager * GetSimulatorManager(void)
Definition: World.h:597
std::unique_ptr< cBlockEntity > OwnedBlockEntity
Definition: BlockEntity.h:16