Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockPiston.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h"
3 #include "BlockPiston.h"
4 #include "../Item.h"
5 #include "../World.h"
6 #include "../Entities/Player.h"
7 #include "../BlockInServerPluginInterface.h"
8 #include "ChunkInterface.h"
9 
10 
11 
12 
13 
14 #define PISTON_MAX_PUSH_DISTANCE 12
15 
16 
17 
18 
20  super(a_BlockType)
21 {
22 }
23 
24 
25 
26 
27 
29  cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
30  Vector3i a_BlockPos,
31  BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta
32 )
33 {
34  // If the piston is extended, destroy the extension as well
35  if (IsExtended(a_OldBlockMeta))
36  {
37  auto extPos = a_BlockPos + MetadataToOffset(a_OldBlockMeta);
38  if (a_ChunkInterface.GetBlock(extPos) == E_BLOCK_PISTON_EXTENSION)
39  {
40  a_ChunkInterface.DropBlockAsPickups(extPos);
41  }
42  }
43 }
44 
45 
46 
47 
48 
50  cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
51  int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
52  int a_CursorX, int a_CursorY, int a_CursorZ,
53  BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
54 )
55 {
56  a_BlockType = m_BlockType;
57  a_BlockMeta = RotationPitchToMetaData(a_Player.GetYaw(), a_Player.GetPitch());
58  return true;
59 }
60 
61 
62 
63 
64 
66 {
67  switch (a_PistonMeta & 0x07)
68  {
69  case 0: return Vector3i( 0, -1, 0);
70  case 1: return Vector3i( 0, 1, 0);
71  case 2: return Vector3i( 0, 0, -1);
72  case 3: return Vector3i( 0, 0, 1);
73  case 4: return Vector3i(-1, 0, 0);
74  case 5: return Vector3i( 1, 0, 0);
75  default:
76  {
77  LOGWARNING("%s: invalid direction %d, ignoring", __FUNCTION__, a_PistonMeta & 0x07);
78  ASSERT(!"Invalid direction");
79  return Vector3i();
80  }
81  }
82 }
83 
84 
85 
86 
87 
89  const Vector3iSet & a_BlocksToPush,
90  cWorld & a_World, const Vector3i & a_PushDir
91 )
92 {
93  // Sort blocks to move the blocks first, which are farest away from the piston
94  // This prevents the overwriting of existing blocks
95  std::vector<Vector3i> sortedBlocks(a_BlocksToPush.begin(), a_BlocksToPush.end());
96  std::sort(sortedBlocks.begin(), sortedBlocks.end(), [a_PushDir](const Vector3i & a, const Vector3i & b)
97  {
98  return (a.Dot(a_PushDir) > b.Dot(a_PushDir));
99  });
100 
101  // Move every block
102  BLOCKTYPE moveBlock;
103  NIBBLETYPE moveMeta;
104  for (auto & moveBlockPos : sortedBlocks)
105  {
106  a_World.GetBlockTypeMeta(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta);
107 
108  if (cBlockInfo::IsPistonBreakable(moveBlock))
109  {
110  // Block is breakable, drop it:
111  a_World.DropBlockAsPickups(moveBlockPos, nullptr, nullptr);
112  }
113  else
114  {
115  // Not breakable, just move it
116  a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0);
117  moveBlockPos += a_PushDir;
118  a_World.SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta);
119  }
120  }
121 }
122 
123 
124 
125 
126 
128  const Vector3i & a_BlockPos, cWorld & a_World, bool a_RequirePushable,
129  Vector3iSet & a_BlocksPushed, const Vector3i & a_PushDir
130 )
131 {
132  const static std::array<Vector3i, 6> pushingDirs =
133  {
134  {
135  Vector3i(-1, 0, 0), Vector3i(1, 0, 0),
136  Vector3i( 0, -1, 0), Vector3i(0, 1, 0),
137  Vector3i( 0, 0, -1), Vector3i(0, 0, 1)
138  }
139  };
140 
141  BLOCKTYPE currBlock;
142  NIBBLETYPE currMeta;
143  a_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, currBlock, currMeta);
144 
145  if (currBlock == E_BLOCK_AIR)
146  {
147  // Air can be pushed
148  return true;
149  }
150 
151  if (!a_RequirePushable && cBlockInfo::IsPistonBreakable(currBlock))
152  {
153  // Block should not be broken, when it's not in the pushing direction
154  return true;
155  }
156 
157  if (!CanPush(currBlock, currMeta))
158  {
159  // When it's not required to push this block, don't fail
160  return !a_RequirePushable;
161  }
162 
163  if (a_BlocksPushed.size() >= PISTON_MAX_PUSH_DISTANCE)
164  {
165  // Do not allow to push too much blocks
166  return false;
167  }
168 
169  if (!a_BlocksPushed.insert(a_BlockPos).second || cBlockInfo::IsPistonBreakable(currBlock))
170  {
171  return true; // Element exist already
172  }
173 
174  if (currBlock == E_BLOCK_SLIME_BLOCK)
175  {
176  // Try to push the other directions
177  for (const auto & testDir : pushingDirs)
178  {
179  if (!CanPushBlock(a_BlockPos + testDir, a_World, false, a_BlocksPushed, a_PushDir))
180  {
181  // When it's not possible for a direction, then fail
182  return false;
183  }
184  }
185  }
186 
187  // Try to push the block in front of this block
188  return CanPushBlock(a_BlockPos + a_PushDir, a_World, true, a_BlocksPushed, a_PushDir);
189 }
190 
191 
192 
193 
194 
196 {
197  {
198  // Broadcast block action first. Will do nothing if piston cannot in fact push
199 
200  BLOCKTYPE pistonBlock;
201  NIBBLETYPE pistonMeta;
202  a_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta);
203  a_World.BroadcastBlockAction(a_BlockPos, PistonExtendAction, pistonMeta, pistonBlock);
204  }
205 
206  // Client expects the server to "play" the animation before setting the final blocks
207  // However, we don't confuse animation with the underlying state of the world, so emulate by delaying 1 tick
208  // (Probably why vanilla has so many dupe glitches with sand and pistons lolol)
209 
210  a_World.ScheduleTask(1, [a_BlockPos](cWorld & World)
211  {
212  BLOCKTYPE pistonBlock;
213  NIBBLETYPE pistonMeta;
214  World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta);
215 
216  if ((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
217  {
218  // Ensure we operate on a piston to avoid spurious behaviour
219  // Note that the scheduled task may result in the block type of a_BlockPos changing
220  return;
221  }
222 
223  if (IsExtended(pistonMeta))
224  {
225  // Already extended, bail out
226  return;
227  }
228 
229  Vector3i pushDir = MetadataToOffset(pistonMeta);
230  Vector3iSet blocksPushed;
231  if (!CanPushBlock(a_BlockPos + pushDir, World, true, blocksPushed, pushDir))
232  {
233  // Can't push anything, bail out
234  return;
235  }
236  PushBlocks(blocksPushed, World, pushDir);
237 
238  // Set the extension and the piston base correctly
239  Vector3i extensionPos = a_BlockPos + pushDir;
240  World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta | 0x8);
241  World.SetBlock(
242  extensionPos.x, extensionPos.y, extensionPos.z,
243  E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0)
244  );
245 
246  // Play sound effect only if extended successfully
247  World.BroadcastSoundEffect("block.piston.extend", a_BlockPos, 0.5f, 0.7f);
248  }
249  );
250 }
251 
252 
253 
254 
255 
257 {
258  {
259  BLOCKTYPE pistonBlock;
260  NIBBLETYPE pistonMeta;
261  a_World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta);
262  a_World.BroadcastBlockAction(a_BlockPos, PistonRetractAction, pistonMeta, pistonBlock);
263  }
264 
265  a_World.ScheduleTask(1, [a_BlockPos](cWorld & World)
266  {
267  BLOCKTYPE pistonBlock;
268  NIBBLETYPE pistonMeta;
269  World.GetBlockTypeMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta);
270 
271  if ((pistonBlock != E_BLOCK_PISTON) && !IsSticky(pistonBlock))
272  {
273  // Ensure we operate on a piston to avoid spurious behaviour
274  // Note that the scheduled task may result in the block type of a_BlockPos changing
275  return;
276  }
277 
278  if (!IsExtended(pistonMeta))
279  {
280  // Already retracted, bail out
281  return;
282  }
283 
284  Vector3i pushDir = MetadataToOffset(pistonMeta);
285 
286  // Check the extension:
287  Vector3i extensionPos = a_BlockPos + pushDir;
288  if (World.GetBlock(extensionPos) != E_BLOCK_PISTON_EXTENSION)
289  {
290  LOGD("%s: Piston without an extension - still extending, or just in an invalid state?", __FUNCTION__);
291  return;
292  }
293 
294  // Remove extension, update base state
295  World.SetBlock(extensionPos.x, extensionPos.y, extensionPos.z, E_BLOCK_AIR, 0);
296  World.SetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, pistonBlock, pistonMeta & ~(8));
297 
298  // (Retraction is always successful, but play in the task for consistency)
299  World.BroadcastSoundEffect("block.piston.contract", a_BlockPos, 0.5f, 0.7f);
300 
301  if (!IsSticky(pistonBlock))
302  {
303  // No need for block pulling, bail out
304  return;
305  }
306 
307  // Get the block to pull
308  Vector3i AdjustedPosition = a_BlockPos + pushDir * 2;
309  // Try to "push" the pulling block in the opposite direction
310  pushDir *= -1;
311 
312  Vector3iSet pushedBlocks;
313  if (!CanPushBlock(AdjustedPosition, World, false, pushedBlocks, pushDir))
314  {
315  // Not pushable, bail out
316  return;
317  }
318 
319  PushBlocks(pushedBlocks, World, pushDir);
320  }
321  );
322 }
323 
324 
325 
326 
327 
329 // cBlockPistonHeadHandler:
330 
333 {
334 }
335 
336 
337 
338 
339 
341  cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
342  Vector3i a_BlockPos,
343  BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta
344 )
345 {
346  // Drop the base of the piston:
347  auto basePos = a_BlockPos - cBlockPistonHandler::MetadataToOffset(a_OldBlockMeta);
348  if (cChunkDef::IsValidHeight(basePos.y))
349  {
350  a_ChunkInterface.DropBlockAsPickups(basePos);
351  }
352 }
353 
354 
355 
356 
357 
static void ExtendPiston(Vector3i a_BlockPos, cWorld &a_World)
static NIBBLETYPE RotationPitchToMetaData(double a_Rotation, double a_Pitch)
Definition: BlockPiston.h:41
double GetPitch(void) const
Definition: Entity.h:210
BLOCKTYPE GetBlock(Vector3i a_BlockPos)
Returns the block type at the specified position.
Definition: World.h:416
T x
Definition: Vector3.h:17
cBlockPistonHandler(BLOCKTYPE a_BlockType)
Definition: BlockPiston.cpp:19
T Dot(const Vector3< T > &a_Rhs) const
Definition: Vector3.h:105
static bool IsExtended(NIBBLETYPE a_PistonMeta)
Returns true if the piston (with the specified meta) is extended.
Definition: BlockPiston.h:109
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
static bool CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Returns true if the specified block can be pushed by a piston (and left intact)
Definition: BlockPiston.h:125
virtual void OnBroken(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override
Called after a block gets broken (replaced with air), either by player or by natural means...
Definition: BlockPiston.cpp:28
static bool IsValidHeight(int a_Height)
Validates a height-coordinate.
Definition: ChunkDef.h:212
Definition: Player.h:27
BLOCKTYPE m_BlockType
Definition: BlockHandler.h:213
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
bool DropBlockAsPickups(Vector3i a_BlockPos, const cEntity *a_Digger=nullptr, const cItem *a_Tool=nullptr)
Digs the specified block, and spawns the appropriate pickups for it.
Definition: World.cpp:2200
#define PISTON_MAX_PUSH_DISTANCE
Definition: BlockPiston.cpp:14
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
virtual void BroadcastSoundEffect(const AString &a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch, const cClientHandle *a_Exclude=nullptr) override
virtual bool GetPlacementBlockTypeMeta(cChunkInterface &a_ChunkInterface, cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) override
Called before a block is placed into a world.
Definition: BlockPiston.cpp:49
Utilities to allow casting a cWorld to one of its interfaces without including World.h.
Definition: OpaqueWorld.h:12
static bool CanPushBlock(const Vector3i &a_BlockPos, cWorld &a_World, bool a_RequirePushable, Vector3iSet &a_BlocksPushed, const Vector3i &a_PushDir)
Tries to push a block and increases the pushed blocks variable.
static bool IsSticky(BLOCKTYPE a_BlockType)
Returns true if the piston (specified by blocktype) is a sticky piston.
Definition: BlockPiston.h:122
bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta)
Retrieves the block type and meta at the specified coords.
Definition: World.cpp:1909
Definition: World.h:65
static const Byte PistonExtendAction
Piston extension block action.
Definition: BlockPiston.h:116
virtual void BroadcastBlockAction(Vector3i a_BlockPos, Byte a_Byte1, Byte a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle *a_Exclude=nullptr) override
static const Byte PistonRetractAction
Piston retraction block action.
Definition: BlockPiston.h:119
#define ASSERT(x)
Definition: Globals.h:335
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
#define LOGD(...)
Definition: LoggerSimple.h:40
void ScheduleTask(int a_DelayTicks, std::function< void(cWorld &)> a_Task)
Queues a lambda task onto the tick thread, with the specified delay.
Definition: World.cpp:3020
void DropBlockAsPickups(Vector3i a_BlockPos, const cEntity *a_Digger=nullptr, const cItem *a_Tool=nullptr)
Digs the block and spawns the relevant pickups, as if a_Digger used a_Tool to dig the block...
double GetYaw(void) const
Definition: Entity.h:209
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc...
Definition: Defines.h:29
static void PushBlocks(const Vector3iSet &a_BlocksToPush, cWorld &a_World, const Vector3i &a_PushDir)
Moves a list of blocks in a specific direction.
Definition: BlockPiston.cpp:88
BLOCKTYPE GetBlock(Vector3i a_Pos)
static bool IsPistonBreakable(BLOCKTYPE a_Type)
Definition: BlockInfo.h:34
static void RetractPiston(Vector3i a_BlockPos, cWorld &a_World)
virtual void OnBroken(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override
Called after a block gets broken (replaced with air), either by player or by natural means...
Vector3< int > Vector3i
Definition: Vector3.h:447
std::unordered_set< Vector3i, VectorHasher< int > > Vector3iSet
Definition: BlockPiston.h:113
static Vector3i MetadataToOffset(NIBBLETYPE a_PistonMeta)
Converts piston block&#39;s metadata into a unit vector representing the direction in which the piston wi...
Definition: BlockPiston.cpp:65
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