Cuberite
A lightweight, fast and extensible game server for Minecraft
SandSimulator.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 "SandSimulator.h"
5 #include "../World.h"
6 #include "../Defines.h"
7 #include "../Entities/FallingBlock.h"
8 #include "../Chunk.h"
9 #include "../IniFile.h"
10 #include "../EffectID.h"
11 
12 
13 
14 
15 
17  cSimulator(a_World),
18  m_TotalBlocks(0)
19 {
20  m_IsInstantFall = a_IniFile.GetValueSetB("Physics", "SandInstantFall", false);
21 }
22 
23 
24 
25 
26 
27 void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
28 {
29  cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
30  if (ChunkData.empty())
31  {
32  return;
33  }
34 
35  int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
36  int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
37  for (cSandSimulatorChunkData::const_iterator itr = ChunkData.begin(), end = ChunkData.end(); itr != end; ++itr)
38  {
39  BLOCKTYPE BlockType = a_Chunk->GetBlock(itr->x, itr->y, itr->z);
40  if (!IsAllowedBlock(BlockType) || (itr->y <= 0))
41  {
42  continue;
43  }
44 
45  BLOCKTYPE BlockBelow = (itr->y > 0) ? a_Chunk->GetBlock(itr->x, itr->y - 1, itr->z) : E_BLOCK_AIR;
46  if (CanStartFallingThrough(BlockBelow))
47  {
48  if (m_IsInstantFall)
49  {
50  DoInstantFall(a_Chunk, itr->x, itr->y, itr->z);
51  continue;
52  }
53  Vector3i Pos;
54  Pos.x = itr->x + BaseX;
55  Pos.y = itr->y;
56  Pos.z = itr->z + BaseZ;
57  /*
58  FLOGD(
59  "Creating a falling block at {0} of type {1}, block below: {2}",
60  Pos, ItemTypeToString(BlockType), ItemTypeToString(BlockBelow)
61  );
62  */
63 
64  auto FallingBlock = cpp14::make_unique<cFallingBlock>(Pos, BlockType, a_Chunk->GetMeta(itr->x, itr->y, itr->z));
65  auto FallingBlockPtr = FallingBlock.get();
66  if (!FallingBlockPtr->Initialize(std::move(FallingBlock), m_World))
67  {
68  continue;
69  }
70  a_Chunk->SetBlock({itr->x, itr->y, itr->z}, E_BLOCK_AIR, 0);
71  }
72  }
73  m_TotalBlocks -= static_cast<int>(ChunkData.size());
74  ChunkData.clear();
75 }
76 
77 
78 
79 
80 
82 {
83  switch (a_BlockType)
84  {
85  case E_BLOCK_ANVIL:
87  case E_BLOCK_DRAGON_EGG:
88  case E_BLOCK_GRAVEL:
89  case E_BLOCK_SAND:
90  {
91  return true;
92  }
93  default:
94  {
95  return false;
96  }
97  }
98 }
99 
100 
101 
102 
103 
104 void cSandSimulator::AddBlock(Vector3i a_Block, cChunk * a_Chunk)
105 {
106  if ((a_Chunk == nullptr) || !a_Chunk->IsValid())
107  {
108  return;
109  }
110  int RelX = a_Block.x - a_Chunk->GetPosX() * cChunkDef::Width;
111  int RelZ = a_Block.z - a_Chunk->GetPosZ() * cChunkDef::Width;
112  if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_Block.y, RelZ)))
113  {
114  return;
115  }
116 
117  // Check for duplicates:
118  cSandSimulatorChunkData & ChunkData = a_Chunk->GetSandSimulatorData();
119  for (cSandSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
120  {
121  if ((itr->x == RelX) && (itr->y == a_Block.y) && (itr->z == RelZ))
122  {
123  return;
124  }
125  }
126 
127  m_TotalBlocks += 1;
128  ChunkData.push_back(cCoordWithInt(RelX, a_Block.y, RelZ));
129 }
130 
131 
132 
133 
134 
136 {
137  // Please keep the list alpha-sorted
138  switch (a_BlockType)
139  {
140  case E_BLOCK_AIR:
141  case E_BLOCK_FIRE:
142  case E_BLOCK_LAVA:
143  case E_BLOCK_SNOW:
146  case E_BLOCK_WATER:
147  {
148  return true;
149  }
150  }
151  return false;
152 }
153 
154 
155 
156 
157 
159 {
160  // Please keep the list alpha-sorted
161  switch (a_BlockType)
162  {
163  case E_BLOCK_AIR:
164  case E_BLOCK_BEETROOTS:
166  case E_BLOCK_CARROTS:
167  case E_BLOCK_COBWEB:
168  case E_BLOCK_CROPS:
169  case E_BLOCK_DEAD_BUSH:
171  case E_BLOCK_FIRE:
172  case E_BLOCK_FLOWER_POT:
175  case E_BLOCK_LAVA:
176  case E_BLOCK_LEVER:
179  case E_BLOCK_MELON_STEM:
188  case E_BLOCK_RED_ROSE:
189  case E_BLOCK_SIGN_POST:
190  case E_BLOCK_SNOW:
196  case E_BLOCK_TALL_GRASS:
197  case E_BLOCK_TORCH:
198  case E_BLOCK_TRAPDOOR:
199  case E_BLOCK_TRIPWIRE:
201  case E_BLOCK_WALL_BANNER:
202  case E_BLOCK_WALLSIGN:
203  case E_BLOCK_WATER:
207  {
208  return true;
209  }
210  }
211  return false;
212 }
213 
214 
215 
216 
217 
219 {
220  // Please keep the list alpha-sorted
221  switch (a_BlockType)
222  {
223  case E_BLOCK_AIR:
226  case E_BLOCK_DEAD_BUSH:
227  case E_BLOCK_FIRE:
228  case E_BLOCK_LAVA:
229  case E_BLOCK_SNOW:
233  case E_BLOCK_TALL_GRASS:
234  case E_BLOCK_WATER:
235  {
236  return true;
237  }
238  }
239  return false;
240 }
241 
242 
243 
244 
245 
247 {
248  switch (a_BlockType)
249  {
250  case E_BLOCK_PURPUR_SLAB:
252  case E_BLOCK_STONE_SLAB:
253  case E_BLOCK_WOODEN_SLAB:
254  {
255  return ((a_BlockMeta & 0x08) == 0); // Only a bottom-slab breaks the block
256  }
257  }
258  return false;
259 }
260 
261 
262 
263 
264 
266  cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ,
267  BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta
268 )
269 {
270  ASSERT(a_BlockY < cChunkDef::Height);
271 
272  BLOCKTYPE CurrentBlockType = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
273  if ((a_FallingBlockType == E_BLOCK_ANVIL) || IsReplacedOnRematerialization(CurrentBlockType))
274  {
275  // Rematerialize the material here:
276  a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FallingBlockType, a_FallingBlockMeta);
277  if (a_FallingBlockType == E_BLOCK_ANVIL)
278  {
279  a_World->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_ANVIL_LAND, {a_BlockX, a_BlockY, a_BlockZ}, 0);
280  }
281  return;
282  }
283 
284  // Create a pickup instead:
285  cItems Pickups;
286  Pickups.Add(static_cast<ENUM_ITEM_ID>(a_FallingBlockType), 1, a_FallingBlockMeta);
287  a_World->SpawnItemPickups(
288  Pickups,
289  static_cast<double>(a_BlockX) + 0.5,
290  static_cast<double>(a_BlockY) + 0.5,
291  static_cast<double>(a_BlockZ) + 0.5
292  );
293 }
294 
295 
296 
297 
298 
299 void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
300 {
301  // Remove the original block:
302  BLOCKTYPE FallingBlockType;
303  NIBBLETYPE FallingBlockMeta;
304  a_Chunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, FallingBlockType, FallingBlockMeta);
305  a_Chunk->SetBlock({a_RelX, a_RelY, a_RelZ}, E_BLOCK_AIR, 0);
306 
307  // Search for a place to put it:
308  for (int y = a_RelY - 1; y >= 0; y--)
309  {
310  BLOCKTYPE BlockType;
311  NIBBLETYPE BlockMeta;
312  a_Chunk->GetBlockTypeMeta(a_RelX, y, a_RelZ, BlockType, BlockMeta);
313  int BlockY;
314  if (DoesBreakFallingThrough(BlockType, BlockMeta))
315  {
316  BlockY = y;
317  }
318  else if (!CanContinueFallThrough(BlockType))
319  {
320  BlockY = y + 1;
321  }
322  else if ((FallingBlockType == E_BLOCK_CONCRETE_POWDER) && IsBlockWater(BlockType))
323  {
324  FallingBlockType = E_BLOCK_CONCRETE;
325  BlockY = y;
326  }
327  else
328  {
329  // Can fall further down
330  continue;
331  }
332 
333  // Finish the fall at the found bottom:
334  int BlockX = a_RelX + a_Chunk->GetPosX() * cChunkDef::Width;
335  int BlockZ = a_RelZ + a_Chunk->GetPosZ() * cChunkDef::Width;
336  FinishFalling(&m_World, BlockX, BlockY, BlockZ, FallingBlockType, FallingBlockMeta);
337  return;
338  }
339 
340  // The block just "fell off the world" without leaving a trace
341 }
342 
343 
344 
345 
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:72
BLOCKTYPE GetBlock(Vector3i a_BlockPos)
Returns the block type at the specified position.
Definition: World.h:416
T x
Definition: Vector3.h:17
static bool IsReplacedOnRematerialization(BLOCKTYPE a_BlockType)
Returns true if the falling block rematerializing will replace the specified block type (e...
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: Defines.h:436
static void FinishFalling(cWorld *a_World, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta)
Called when a block finishes falling at the specified coords, either by insta-fall, or through cFallingBlock entity.
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
static const int Width
Definition: ChunkDef.h:134
void SetBlock(Vector3i a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Definition: Chunk.cpp:1313
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
cSandSimulatorChunkData & GetSandSimulatorData(void)
Definition: Chunk.h:532
virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk *a_Chunk) override
Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct acce...
Definition: Chunk.h:49
void SpawnItemPickups(const cItems &a_Pickups, Vector3i a_BlockPos, double a_FlyAwaySpeed=1.0, bool a_IsPlayerCreated=false)
Spawns item pickups for each item in the list.
Definition: World.cpp:1936
cWorld & m_World
Definition: Simulator.h:60
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
static const int Height
Definition: ChunkDef.h:135
Base class for all block-based physics simulators (such as fluid, fire, falling blocks etc...
Definition: Simulator.h:19
virtual void BroadcastSoundParticleEffect(const EffectID a_EffectID, Vector3i a_SrcPos, int a_Data, const cClientHandle *a_Exclude=nullptr) override
Definition: World.h:65
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:380
void Add(const cItem &a_Item)
Definition: Item.h:254
cSandSimulator(cWorld &a_World, cIniFile &a_IniFile)
int GetPosX(void) const
Definition: Chunk.h:150
cCoordWithIntList cSandSimulatorChunkData
Per-chunk data for the simulator, specified individual chunks to simulate; Data is not used...
Definition: SandSimulator.h:11
#define ASSERT(x)
Definition: Globals.h:335
static bool CanStartFallingThrough(BLOCKTYPE a_BlockType)
Returns true if a falling-able block can start falling through the specified block type...
static bool CanContinueFallThrough(BLOCKTYPE a_BlockType)
Returns true if an already-falling block can pass through the specified block type (e...
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override
Returns true if the specified block type is "interesting" for this simulator.
virtual void AddBlock(Vector3i a_Block, cChunk *a_Chunk) override
Called to simulate a new block.
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:177
int GetPosZ(void) const
Definition: Chunk.h:151
void DoInstantFall(cChunk *a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
Performs the instant fall of the block - removes it from top, Finishes it at the bottom.
bool GetValueSetB(const AString &keyname, const AString &valuename, const bool defValue=false) override
Definition: IniFile.h:145
static bool DoesBreakFallingThrough(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Returns true if the specified block breaks falling blocks while they fall through it (e...
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:1873
cCoordWithData< int > cCoordWithInt
Definition: ChunkDef.h:665
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:234