2026-03-01 12:16:08 +08:00
# include "stdafx.h"
# include "PistonBaseTile.h"
# include "PistonMovingPiece.h"
# include "PistonPieceEntity.h"
# include "PistonExtensionTile.h"
# include "Facing.h"
# include "net.minecraft.world.level.h"
# include "..\Minecraft.Client\Minecraft.h"
# include "..\Minecraft.Client\MultiPlayerLevel.h"
# include "net.minecraft.world.h"
# include "LevelChunk.h"
# include "Dimension.h"
const wstring PistonBaseTile : : EDGE_TEX = L " piston_side " ;
const wstring PistonBaseTile : : PLATFORM_TEX = L " piston_top " ;
const wstring PistonBaseTile : : PLATFORM_STICKY_TEX = L " piston_top_sticky " ;
const wstring PistonBaseTile : : BACK_TEX = L " piston_bottom " ;
const wstring PistonBaseTile : : INSIDE_TEX = L " piston_inner_top " ;
const float PistonBaseTile : : PLATFORM_THICKNESS = 4.0f ;
DWORD PistonBaseTile : : tlsIdx = TlsAlloc ( ) ;
// 4J - NOTE - this ignoreUpdate stuff has been removed from the java version, but I'm not currently sure how the java version does without it... there must be
// some other mechanism that we don't have that stops the event from one piston being processed, from causing neighbours to have extra events created for them.
// For us, that means that if we create a piston next to another one, then one of them gets two events to createPush, the second of which fails, leaving the
// piston in a bad (simultaneously extended & not extended) state.
// 4J - ignoreUpdate is a static in java, implementing as TLS here to make thread safe
bool PistonBaseTile : : ignoreUpdate ( )
{
return ( TlsGetValue ( tlsIdx ) ! = NULL ) ;
}
void PistonBaseTile : : ignoreUpdate ( bool set )
{
TlsSetValue ( tlsIdx , ( LPVOID ) ( set ? 1 : 0 ) ) ;
}
PistonBaseTile : : PistonBaseTile ( int id , bool isSticky ) : Tile ( id , Material : : piston , isSolidRender ( ) )
{
// 4J - added initialiser
ignoreUpdate ( false ) ;
this - > isSticky = isSticky ;
setSoundType ( SOUND_STONE ) ;
setDestroyTime ( 0.5f ) ;
iconInside = NULL ;
iconBack = NULL ;
iconPlatform = NULL ;
}
Icon * PistonBaseTile : : getPlatformTexture ( )
{
return iconPlatform ;
}
void PistonBaseTile : : updateShape ( float x0 , float y0 , float z0 , float x1 , float y1 , float z1 )
{
setShape ( x0 , y0 , z0 , x1 , y1 , z1 ) ;
}
Icon * PistonBaseTile : : getTexture ( int face , int data )
{
int facing = getFacing ( data ) ;
if ( facing > 5 )
{
return iconPlatform ;
}
if ( face = = facing )
{
// sorry about this mess...
// when the piston is extended, either normally
// or because a piston arm animation, the top
// texture is the furnace bottom
ThreadStorage * tls = ( ThreadStorage * ) TlsGetValue ( Tile : : tlsIdxShape ) ;
if ( isExtended ( data ) | | tls - > xx0 > 0 | | tls - > yy0 > 0 | | tls - > zz0 > 0 | | tls - > xx1 < 1 | | tls - > yy1 < 1 | | tls - > zz1 < 1 )
{
return iconInside ;
}
return iconPlatform ;
}
if ( face = = Facing : : OPPOSITE_FACING [ facing ] )
{
return iconBack ;
}
return icon ;
}
Icon * PistonBaseTile : : getTexture ( const wstring & name )
{
if ( name . compare ( EDGE_TEX ) = = 0 ) return Tile : : pistonBase - > icon ;
if ( name . compare ( PLATFORM_TEX ) = = 0 ) return Tile : : pistonBase - > iconPlatform ;
if ( name . compare ( PLATFORM_STICKY_TEX ) = = 0 ) return Tile : : pistonStickyBase - > iconPlatform ;
if ( name . compare ( INSIDE_TEX ) = = 0 ) return Tile : : pistonBase - > iconInside ;
return NULL ;
}
//@Override
void PistonBaseTile : : registerIcons ( IconRegister * iconRegister )
{
icon = iconRegister - > registerIcon ( EDGE_TEX ) ;
iconPlatform = iconRegister - > registerIcon ( isSticky ? PLATFORM_STICKY_TEX : PLATFORM_TEX ) ;
iconInside = iconRegister - > registerIcon ( INSIDE_TEX ) ;
iconBack = iconRegister - > registerIcon ( BACK_TEX ) ;
}
int PistonBaseTile : : getRenderShape ( )
{
return SHAPE_PISTON_BASE ;
}
bool PistonBaseTile : : isSolidRender ( bool isServerLevel )
{
return false ;
}
2026-03-02 17:37:16 +07:00
bool PistonBaseTile : : use ( Level * level , int x , int y , int z , shared_ptr < Player > player , int clickedFace , float clickX , float clickY , float clickZ , bool soundOnly /*=false*/ ) // 4J added soundOnly param
2026-03-01 12:16:08 +08:00
{
return false ;
}
2026-03-02 17:37:16 +07:00
void PistonBaseTile : : setPlacedBy ( Level * level , int x , int y , int z , shared_ptr < Mob > by )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:36:56 +07:00
int targetData = getNewFacing ( level , x , y , z , dynamic_pointer_cast < Player > ( by ) ) ;
2026-03-01 12:16:08 +08:00
level - > setData ( x , y , z , targetData ) ;
if ( ! level - > isClientSide & & ! ignoreUpdate ( ) )
{
checkIfExtend ( level , x , y , z ) ;
}
}
void PistonBaseTile : : neighborChanged ( Level * level , int x , int y , int z , int type )
{
if ( ! level - > isClientSide & & ! ignoreUpdate ( ) )
{
checkIfExtend ( level , x , y , z ) ;
}
}
void PistonBaseTile : : onPlace ( Level * level , int x , int y , int z )
{
if ( ! level - > isClientSide & & level - > getTileEntity ( x , y , z ) = = NULL & & ! ignoreUpdate ( ) )
{
checkIfExtend ( level , x , y , z ) ;
}
}
void PistonBaseTile : : checkIfExtend ( Level * level , int x , int y , int z )
{
int data = level - > getData ( x , y , z ) ;
int facing = getFacing ( data ) ;
if ( facing = = UNDEFINED_FACING )
{
return ;
}
bool extend = getNeighborSignal ( level , x , y , z , facing ) ;
if ( extend & & ! isExtended ( data ) )
{
if ( canPush ( level , x , y , z , facing ) )
{
//level->setDataNoUpdate(x, y, z, facing | EXTENDED_BIT);
level - > tileEvent ( x , y , z , id , TRIGGER_EXTEND , facing ) ;
}
}
else if ( ! extend & & isExtended ( data ) )
{
//level->setDataNoUpdate(x, y, z, facing);
level - > tileEvent ( x , y , z , id , TRIGGER_CONTRACT , facing ) ;
}
}
/**
* This method checks neighbor signals for this block and the block above ,
* and directly beneath . However , it avoids checking blocks that would be
* pushed by this block .
2026-03-02 17:37:16 +07:00
*
2026-03-01 12:16:08 +08:00
* @ param level
* @ param x
* @ param y
* @ param z
* @ return
*/
bool PistonBaseTile : : getNeighborSignal ( Level * level , int x , int y , int z , int facing )
{
// check adjacent neighbors, but not in push direction
if ( facing ! = Facing : : DOWN & & level - > getSignal ( x , y - 1 , z , Facing : : DOWN ) ) return true ;
if ( facing ! = Facing : : UP & & level - > getSignal ( x , y + 1 , z , Facing : : UP ) ) return true ;
if ( facing ! = Facing : : NORTH & & level - > getSignal ( x , y , z - 1 , Facing : : NORTH ) ) return true ;
if ( facing ! = Facing : : SOUTH & & level - > getSignal ( x , y , z + 1 , Facing : : SOUTH ) ) return true ;
if ( facing ! = Facing : : EAST & & level - > getSignal ( x + 1 , y , z , Facing : : EAST ) ) return true ;
if ( facing ! = Facing : : WEST & & level - > getSignal ( x - 1 , y , z , Facing : : WEST ) ) return true ;
// check signals above
if ( level - > getSignal ( x , y , z , 0 ) ) return true ;
if ( level - > getSignal ( x , y + 2 , z , 1 ) ) return true ;
if ( level - > getSignal ( x , y + 1 , z - 1 , 2 ) ) return true ;
if ( level - > getSignal ( x , y + 1 , z + 1 , 3 ) ) return true ;
if ( level - > getSignal ( x - 1 , y + 1 , z , 4 ) ) return true ;
if ( level - > getSignal ( x + 1 , y + 1 , z , 5 ) ) return true ;
return false ;
}
void PistonBaseTile : : triggerEvent ( Level * level , int x , int y , int z , int param1 , int facing )
{
ignoreUpdate ( true ) ;
if ( param1 = = TRIGGER_EXTEND )
{
level - > setDataNoUpdate ( x , y , z , facing | EXTENDED_BIT ) ;
}
else
{
level - > setDataNoUpdate ( x , y , z , facing ) ;
}
if ( param1 = = TRIGGER_EXTEND )
{
PIXBeginNamedEvent ( 0 , " Create push \n " ) ;
if ( createPush ( level , x , y , z , facing ) )
{
// 4J - it is (currently) critical that this setData sends data to the client, so have added a bool to the method so that it sends data even if the data was already set to the same value
// as before, which was actually its behaviour until a change in 1.0.1 meant that setData only conditionally sent updates to listeners. If the data update Isn't sent, then what
// can happen is:
// (1) the host sends the tile event to the client
// (2) the client gets the tile event, and sets the tile/data value locally.
// (3) just before setting the tile/data locally, the client will put the old value in the vector of things to be restored should an update not be received back from the host
// (4) we don't get any update of the tile from the host, and so the old value gets restored on the client
// (5) the piston base ends up being restored to its retracted state whilst the piston arm is extended
// We really need to spend some time investigating a better way for pistons to work as it all seems a bit scary how the host/client interact, but forcing this to send should at least
// restore the behaviour of the pistons to something closer to what they were before the 1.0.1 update. By sending this data update, then (4) in the list above doesn't happen
// because the client does actually receive an update for this tile from the host after the event has been processed on the cient.
level - > setData ( x , y , z , facing | EXTENDED_BIT , true ) ;
level - > playSound ( x + 0.5 , y + 0.5 , z + 0.5 , eSoundType_TILE_PISTON_OUT , 0.5f , level - > random - > nextFloat ( ) * 0.25f + 0.6f ) ;
}
else
{
level - > setDataNoUpdate ( x , y , z , facing ) ;
}
PIXEndNamedEvent ( ) ;
}
else if ( param1 = = TRIGGER_CONTRACT )
{
PIXBeginNamedEvent ( 0 , " Contract phase A \n " ) ;
2026-03-02 17:37:16 +07:00
shared_ptr < TileEntity > prevTileEntity = level - > getTileEntity ( x + Facing : : STEP_X [ facing ] , y + Facing : : STEP_Y [ facing ] , z + Facing : : STEP_Z [ facing ] ) ;
2026-03-02 17:36:56 +07:00
if ( prevTileEntity ! = NULL & & dynamic_pointer_cast < PistonPieceEntity > ( prevTileEntity ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:36:56 +07:00
dynamic_pointer_cast < PistonPieceEntity > ( prevTileEntity ) - > finalTick ( ) ;
2026-03-01 12:16:08 +08:00
}
stopSharingIfServer ( level , x , y , z ) ; // 4J added
level - > setTileAndDataNoUpdate ( x , y , z , Tile : : pistonMovingPiece_Id , facing ) ;
level - > setTileEntity ( x , y , z , PistonMovingPiece : : newMovingPieceEntity ( id , facing , facing , false , true ) ) ;
PIXEndNamedEvent ( ) ;
// sticky movement
if ( isSticky )
{
PIXBeginNamedEvent ( 0 , " Contract sticky phase A \n " ) ;
int twoX = x + Facing : : STEP_X [ facing ] * 2 ;
int twoY = y + Facing : : STEP_Y [ facing ] * 2 ;
int twoZ = z + Facing : : STEP_Z [ facing ] * 2 ;
int block = level - > getTile ( twoX , twoY , twoZ ) ;
int blockData = level - > getData ( twoX , twoY , twoZ ) ;
bool pistonPiece = false ;
PIXEndNamedEvent ( ) ;
if ( block = = Tile : : pistonMovingPiece_Id )
{
PIXBeginNamedEvent ( 0 , " Contract sticky phase B \n " ) ;
// the block two steps away is a moving piston block piece,
// so replace it with the real data, since it's probably
// this piston which is changing too fast
2026-03-02 17:37:16 +07:00
shared_ptr < TileEntity > tileEntity = level - > getTileEntity ( twoX , twoY , twoZ ) ;
2026-03-02 17:36:56 +07:00
if ( tileEntity ! = NULL & & dynamic_pointer_cast < PistonPieceEntity > ( tileEntity ) ! = NULL )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +07:00
shared_ptr < PistonPieceEntity > ppe = dynamic_pointer_cast < PistonPieceEntity > ( tileEntity ) ;
2026-03-01 12:16:08 +08:00
if ( ppe - > getFacing ( ) = = facing & & ppe - > isExtending ( ) )
{
// force the tile to air before pushing
ppe - > finalTick ( ) ;
block = ppe - > getId ( ) ;
blockData = ppe - > getData ( ) ;
pistonPiece = true ;
}
}
PIXEndNamedEvent ( ) ;
}
PIXBeginNamedEvent ( 0 , " Contract sticky phase C \n " ) ;
if ( ! pistonPiece & & block > 0 & & ( isPushable ( block , level , twoX , twoY , twoZ , false ) )
& & ( Tile : : tiles [ block ] - > getPistonPushReaction ( ) = = Material : : PUSH_NORMAL | | block = = Tile : : pistonBase_Id | | block = = Tile : : pistonStickyBase_Id ) )
{
stopSharingIfServer ( level , twoX , twoY , twoZ ) ; // 4J added
x + = Facing : : STEP_X [ facing ] ;
y + = Facing : : STEP_Y [ facing ] ;
z + = Facing : : STEP_Z [ facing ] ;
level - > setTileAndDataNoUpdate ( x , y , z , Tile : : pistonMovingPiece_Id , blockData ) ;
level - > setTileEntity ( x , y , z , PistonMovingPiece : : newMovingPieceEntity ( block , blockData , facing , false , false ) ) ;
ignoreUpdate ( false ) ;
level - > setTile ( twoX , twoY , twoZ , 0 ) ;
ignoreUpdate ( true ) ;
}
else if ( ! pistonPiece )
{
stopSharingIfServer ( level , x + Facing : : STEP_X [ facing ] , y + Facing : : STEP_Y [ facing ] , z + Facing : : STEP_Z [ facing ] ) ; // 4J added
ignoreUpdate ( false ) ;
level - > setTile ( x + Facing : : STEP_X [ facing ] , y + Facing : : STEP_Y [ facing ] , z + Facing : : STEP_Z [ facing ] , 0 ) ;
ignoreUpdate ( true ) ;
}
PIXEndNamedEvent ( ) ;
}
else
{
stopSharingIfServer ( level , x + Facing : : STEP_X [ facing ] , y + Facing : : STEP_Y [ facing ] , z + Facing : : STEP_Z [ facing ] ) ; // 4J added
ignoreUpdate ( false ) ;
level - > setTile ( x + Facing : : STEP_X [ facing ] , y + Facing : : STEP_Y [ facing ] , z + Facing : : STEP_Z [ facing ] , 0 ) ;
ignoreUpdate ( true ) ;
}
level - > playSound ( x + 0.5 , y + 0.5 , z + 0.5 , eSoundType_TILE_PISTON_IN , 0.5f , level - > random - > nextFloat ( ) * 0.15f + 0.6f ) ;
}
ignoreUpdate ( false ) ;
}
2026-03-02 17:37:16 +07:00
void PistonBaseTile : : updateShape ( LevelSource * level , int x , int y , int z , int forceData , shared_ptr < TileEntity > forceEntity ) // 4J added forceData, forceEntity param
2026-03-01 12:16:08 +08:00
{
int data = ( forceData = = - 1 ) ? level - > getData ( x , y , z ) : forceData ;
if ( isExtended ( data ) )
{
const float thickness = PLATFORM_THICKNESS / 16.0f ;
switch ( getFacing ( data ) )
{
case Facing : : DOWN :
setShape ( 0 , thickness , 0 , 1 , 1 , 1 ) ;
break ;
case Facing : : UP :
setShape ( 0 , 0 , 0 , 1 , 1 - thickness , 1 ) ;
break ;
case Facing : : NORTH :
setShape ( 0 , 0 , thickness , 1 , 1 , 1 ) ;
break ;
case Facing : : SOUTH :
setShape ( 0 , 0 , 0 , 1 , 1 , 1 - thickness ) ;
break ;
case Facing : : WEST :
setShape ( thickness , 0 , 0 , 1 , 1 , 1 ) ;
break ;
case Facing : : EAST :
setShape ( 0 , 0 , 0 , 1 - thickness , 1 , 1 ) ;
break ;
}
}
else
{
setShape ( 0 , 0 , 0 , 1 , 1 , 1 ) ;
}
}
void PistonBaseTile : : updateDefaultShape ( )
{
setShape ( 0 , 0 , 0 , 1 , 1 , 1 ) ;
}
2026-03-02 17:37:16 +07:00
void PistonBaseTile : : addAABBs ( Level * level , int x , int y , int z , AABB * box , AABBList * boxes , shared_ptr < Entity > source )
2026-03-01 12:16:08 +08:00
{
setShape ( 0 , 0 , 0 , 1 , 1 , 1 ) ;
Tile : : addAABBs ( level , x , y , z , box , boxes , source ) ;
}
AABB * PistonBaseTile : : getAABB ( Level * level , int x , int y , int z )
{
updateShape ( level , x , y , z ) ;
return Tile : : getAABB ( level , x , y , z ) ;
}
bool PistonBaseTile : : isCubeShaped ( )
{
return false ;
}
int PistonBaseTile : : getFacing ( int data )
{
return data & 0x7 ;
}
bool PistonBaseTile : : isExtended ( int data )
{
return ( data & EXTENDED_BIT ) ! = 0 ;
}
2026-03-02 17:37:16 +07:00
int PistonBaseTile : : getNewFacing ( Level * level , int x , int y , int z , shared_ptr < Player > player )
2026-03-01 12:16:08 +08:00
{
2026-03-02 17:37:16 +07:00
if ( Mth : : abs ( ( float ) player - > x - x ) < 2 & & Mth : : abs ( ( float ) player - > z - z ) < 2 )
2026-03-01 12:16:08 +08:00
{
// If the player is above the block, the slot is on the top
double py = player - > y + 1.82 - player - > heightOffset ;
if ( py - y > 2 )
{
return Facing : : UP ;
}
// If the player is below the block, the slot is on the bottom
if ( y - py > 0 )
{
return Facing : : DOWN ;
}
}
// The slot is on the side
int i = Mth : : floor ( player - > yRot * 4.0f / 360.0f + 0.5 ) & 0x3 ;
if ( i = = 0 ) return Facing : : NORTH ;
if ( i = = 1 ) return Facing : : EAST ;
if ( i = = 2 ) return Facing : : SOUTH ;
if ( i = = 3 ) return Facing : : WEST ;
return 0 ;
}
bool PistonBaseTile : : isPushable ( int block , Level * level , int cx , int cy , int cz , bool allowDestroyable )
{
// special case for obsidian
if ( block = = Tile : : obsidian_Id )
{
return false ;
}
if ( block = = Tile : : pistonBase_Id | | block = = Tile : : pistonStickyBase_Id )
{
// special case for piston bases
if ( isExtended ( level - > getData ( cx , cy , cz ) ) )
{
return false ;
}
}
else
{
if ( Tile : : tiles [ block ] - > getDestroySpeed ( level , cx , cy , cz ) = = Tile : : INDESTRUCTIBLE_DESTROY_TIME )
{
return false ;
}
if ( Tile : : tiles [ block ] - > getPistonPushReaction ( ) = = Material : : PUSH_BLOCK )
{
return false ;
}
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
if ( ! allowDestroyable & & Tile : : tiles [ block ] - > getPistonPushReaction ( ) = = Material : : PUSH_DESTROY )
{
return false ;
}
}
if ( Tile : : tiles [ block ] - > isEntityTile ( ) ) // 4J - java uses instanceof EntityTile here
{
// may not push tile entities
return false ;
}
return true ;
}
bool PistonBaseTile : : canPush ( Level * level , int sx , int sy , int sz , int facing )
{
int cx = sx + Facing : : STEP_X [ facing ] ;
int cy = sy + Facing : : STEP_Y [ facing ] ;
int cz = sz + Facing : : STEP_Z [ facing ] ;
for ( int i = 0 ; i < MAX_PUSH_DEPTH + 1 ; i + + )
{
if ( cy < = 0 | | cy > = ( Level : : maxBuildHeight - 1 ) )
{
// out of bounds
return false ;
}
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
// 4J - added to also check for out of bounds in x/z for our finite world
int minXZ = - ( level - > dimension - > getXZSize ( ) * 16 ) / 2 ;
int maxXZ = ( level - > dimension - > getXZSize ( ) * 16 ) / 2 - 1 ;
if ( ( cx < = minXZ ) | | ( cx > = maxXZ ) | | ( cz < = minXZ ) | | ( cz > = maxXZ ) )
{
return false ;
}
int block = level - > getTile ( cx , cy , cz ) ;
if ( block = = 0 )
{
break ;
}
if ( ! isPushable ( block , level , cx , cy , cz , true ) )
{
return false ;
}
if ( Tile : : tiles [ block ] - > getPistonPushReaction ( ) = = Material : : PUSH_DESTROY )
{
break ;
}
if ( i = = MAX_PUSH_DEPTH )
{
// we've reached the maximum push depth
// without finding air or a breakable block
return false ;
}
cx + = Facing : : STEP_X [ facing ] ;
cy + = Facing : : STEP_Y [ facing ] ;
cz + = Facing : : STEP_Z [ facing ] ;
}
return true ;
}
void PistonBaseTile : : stopSharingIfServer ( Level * level , int x , int y , int z )
{
if ( ! level - > isClientSide )
2026-03-02 17:37:16 +07:00
{
2026-03-01 12:16:08 +08:00
MultiPlayerLevel * clientLevel = Minecraft : : GetInstance ( ) - > getLevel ( level - > dimension - > id ) ;
if ( clientLevel )
{
LevelChunk * lc = clientLevel - > getChunkAt ( x , z ) ;
lc - > stopSharingTilesAndData ( ) ;
}
}
}
bool PistonBaseTile : : createPush ( Level * level , int sx , int sy , int sz , int facing )
{
int cx = sx + Facing : : STEP_X [ facing ] ;
int cy = sy + Facing : : STEP_Y [ facing ] ;
int cz = sz + Facing : : STEP_Z [ facing ] ;
for ( int i = 0 ; i < MAX_PUSH_DEPTH + 1 ; i + + )
{
if ( cy < = 0 | | cy > = ( Level : : maxBuildHeight - 1 ) )
{
// out of bounds
return false ;
}
2026-03-02 17:37:16 +07:00
2026-03-01 12:16:08 +08:00
// 4J - added to also check for out of bounds in x/z for our finite world
int minXZ = - ( level - > dimension - > getXZSize ( ) * 16 ) / 2 ;
int maxXZ = ( level - > dimension - > getXZSize ( ) * 16 ) / 2 - 1 ;
if ( ( cx < = minXZ ) | | ( cx > = maxXZ ) | | ( cz < = minXZ ) | | ( cz > = maxXZ ) )
{
return false ;
}
int block = level - > getTile ( cx , cy , cz ) ;
if ( block = = 0 )
{
break ;
}
if ( ! isPushable ( block , level , cx , cy , cz , true ) )
{
return false ;
}
if ( Tile : : tiles [ block ] - > getPistonPushReaction ( ) = = Material : : PUSH_DESTROY )
{
// this block is destroyed when pushed
Tile : : tiles [ block ] - > spawnResources ( level , cx , cy , cz , level - > getData ( cx , cy , cz ) , 0 ) ;
// setting the tile to air is actually superflous, but
// helps vs multiplayer problems
stopSharingIfServer ( level , cx , cy , cz ) ; // 4J added
level - > setTile ( cx , cy , cz , 0 ) ;
break ;
}
if ( i = = MAX_PUSH_DEPTH )
{
// we've reached the maximum push depth
// without finding air or a breakable block
return false ;
}
cx + = Facing : : STEP_X [ facing ] ;
cy + = Facing : : STEP_Y [ facing ] ;
cz + = Facing : : STEP_Z [ facing ] ;
}
while ( cx ! = sx | | cy ! = sy | | cz ! = sz )
{
int nx = cx - Facing : : STEP_X [ facing ] ;
int ny = cy - Facing : : STEP_Y [ facing ] ;
int nz = cz - Facing : : STEP_Z [ facing ] ;
int block = level - > getTile ( nx , ny , nz ) ;
int data = level - > getData ( nx , ny , nz ) ;
stopSharingIfServer ( level , cx , cy , cz ) ; // 4J added
if ( block = = id & & nx = = sx & & ny = = sy & & nz = = sz )
{
level - > setTileAndDataNoUpdate ( cx , cy , cz , Tile : : pistonMovingPiece_Id , facing | ( isSticky ? PistonExtensionTile : : STICKY_BIT : 0 ) , false ) ;
level - > setTileEntity ( cx , cy , cz , PistonMovingPiece : : newMovingPieceEntity ( Tile : : pistonExtensionPiece_Id , facing | ( isSticky ? PistonExtensionTile : : STICKY_BIT : 0 ) , facing , true , false ) ) ;
}
else
{
level - > setTileAndDataNoUpdate ( cx , cy , cz , Tile : : pistonMovingPiece_Id , data , false ) ;
level - > setTileEntity ( cx , cy , cz , PistonMovingPiece : : newMovingPieceEntity ( block , data , facing , true , false ) ) ;
}
cx = nx ;
cy = ny ;
cz = nz ;
}
return true ;
}