void cBlockArea::MirrorYZ(void) { if (!HasBlockTypes()) { LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!"); return; } if (!HasBlockMetas()) { // There are no blockmetas to mirror, just use the NoMeta function MirrorYZNoMeta(); return; } // We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time: int HalfX = m_SizeX / 2; int MaxX = m_SizeX - 1; for (int y = 0; y < m_SizeY; y++) { for (int z = 0; z < m_SizeZ; z++) { for (int x = 0; x < HalfX; x++) { int Idx1 = MakeIndex(x, y, z); int Idx2 = MakeIndex(MaxX - x, y, z); std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]); NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]); NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]); m_BlockMetas[Idx1] = Meta2; m_BlockMetas[Idx2] = Meta1; } // for x } // for z } // for y }
void cBlockPistonHandler::ExtendPiston(int pistx, int pisty, int pistz) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; m_World->GetBlockTypeMeta(pistx, pisty, pistz, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(pistx, pisty, pistz, pistonMeta); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } m_World->BroadcastBlockAction(pistx, pisty, pistz, 0, pistonMeta, pistonBlock); m_World->BroadcastSoundEffect("tile.piston.out", pistx * 8, pisty * 8, pistz * 8, 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(pistx, pisty, pistz, pistonMeta, dist + 1); // "pist" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(m_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*m_World); Handler->DropBlock(ChunkInterface, *m_World, PluginInterface, NULL, pistx, pisty, pistz); } } // Push blocks, from the furthest to the nearest: int oldx = pistx, oldy = pisty, oldz = pistz; NIBBLETYPE currBlockMeta; for (int i = dist + 1; i > 1; i--) { AddPistonDir(pistx, pisty, pistz, pistonMeta, -1); m_World->GetBlockTypeMeta(pistx, pisty, pistz, currBlock, currBlockMeta); m_World->QueueSetBlock( oldx, oldy, oldz, currBlock, currBlockMeta, PISTON_TICK_DELAY); oldx = pistx; oldy = pisty; oldz = pistz; } int extx = pistx; int exty = pisty; int extz = pistz; AddPistonDir(pistx, pisty, pistz, pistonMeta, -1); // "pist" now at piston body, "ext" at future extension m_World->SetBlock(pistx, pisty, pistz, pistonBlock, pistonMeta | 0x8); m_World->QueueSetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), PISTON_TICK_DELAY); }
void cBlockPistonHandler::PushBlocks( const Vector3iSet & a_BlocksToPush, cWorld * a_World, const Vector3i & a_PushDir ) { // Sort blocks to move the blocks first, which are farest away from the piston // This prevents the overwriting of existing blocks std::vector<Vector3i> sortedBlocks(a_BlocksToPush.begin(), a_BlocksToPush.end()); std::sort(sortedBlocks.begin(), sortedBlocks.end(), [a_PushDir](const Vector3i & a, const Vector3i & b) { return a.Dot(a_PushDir) > b.Dot(a_PushDir); }); // Move every block BLOCKTYPE moveBlock; NIBBLETYPE moveMeta; for (auto & moveBlockPos : sortedBlocks) { a_World->GetBlockTypeMeta(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta); a_World->SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, E_BLOCK_AIR, 0); moveBlockPos += a_PushDir; if (cBlockInfo::IsPistonBreakable(moveBlock)) { // Block is breakable, drop it cBlockHandler * Handler = BlockHandler(moveBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, nullptr, moveBlockPos.x, moveBlockPos.y, moveBlockPos.z ); } } else { // Not breakable, just move it a_World->SetBlock(moveBlockPos.x, moveBlockPos.y, moveBlockPos.z, moveBlock, moveMeta); } } }
void cBlockArea::RotateCW(void) { if (!HasBlockTypes()) { LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!"); return; } if (!HasBlockMetas()) { // There are no blockmetas to rotate, just use the NoMeta function RotateCWNoMeta(); return; } // We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time: BLOCKTYPE * NewTypes = new BLOCKTYPE[m_SizeX * m_SizeY * m_SizeZ]; NIBBLETYPE * NewMetas = new NIBBLETYPE[m_SizeX * m_SizeY * m_SizeZ]; for (int x = 0; x < m_SizeX; x++) { int NewZ = x; for (int z = 0; z < m_SizeZ; z++) { int NewX = m_SizeZ - z - 1; for (int y = 0; y < m_SizeY; y++) { int NewIdx = NewX + NewZ * m_SizeX + y * m_SizeX * m_SizeZ; int OldIdx = MakeIndex(x, y, z); NewTypes[NewIdx] = m_BlockTypes[OldIdx]; NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]); } // for y } // for z } // for x std::swap(m_BlockTypes, NewTypes); std::swap(m_BlockMetas, NewMetas); delete[] NewTypes; delete[] NewMetas; std::swap(m_SizeX, m_SizeZ); }
bool cItemHandler::GetPlacementBlockTypeMeta( cWorld * a_World, 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 ) { ASSERT(m_ItemType < 256); // Items with IDs above 255 should all be handled by specific handlers if (m_ItemType >= 256) { LOGERROR("%s: Item %d is not eligible for direct block placement!", __FUNCTION__, m_ItemType); return false; } cBlockHandler * BlockH = BlockHandler((BLOCKTYPE)m_ItemType); cChunkInterface ChunkInterface(a_World->GetChunkMap()); return BlockH->GetPlacementBlockTypeMeta( ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta ); }
int WaitForSomething(int *pClientsReady) { struct timeval *wt, waittime; fd_set clientsReadable; fd_set clientsWriteable; long curclient; int selecterr; long current_time = 0; long timeout; int nready, i; while (1) { /* handle the work Q */ if (workQueue) ProcessWorkQueue(); if (XFD_ANYSET(&ClientsWithInput)) { XFD_COPYSET(&ClientsWithInput, &clientsReadable); break; } /* * deal with KeepAlive timeouts. if this seems to costly, SIGALRM * could be used, but its more dangerous since some it could catch us * at an inopportune moment (like inside un-reentrant malloc()). */ current_time = GetTimeInMillis(); timeout = current_time - LastReapTime; if (timeout > ReapClientTime) { ReapAnyOldClients(); LastReapTime = current_time; timeout = ReapClientTime; } timeout = ReapClientTime - timeout; waittime.tv_sec = timeout / MILLI_PER_SECOND; waittime.tv_usec = (timeout % MILLI_PER_SECOND) * (1000000 / MILLI_PER_SECOND); wt = &waittime; XFD_COPYSET(&AllSockets, &LastSelectMask); BlockHandler(&wt, (pointer) &LastSelectMask); if (NewOutputPending) FlushAllOutput(); if (AnyClientsWriteBlocked) { XFD_COPYSET(&ClientsWriteBlocked, &clientsWriteable); i = Select(MAXSOCKS, &LastSelectMask, &clientsWriteable, NULL, wt); } else { i = Select(MAXSOCKS, &LastSelectMask, NULL, NULL, wt); } selecterr = errno; WakeupHandler(i, (unsigned long *) &LastSelectMask); if (i <= 0) { /* error or timeout */ FD_ZERO(&clientsWriteable); if (i < 0) { if (selecterr == EBADF) { /* somebody disconnected */ CheckConnections(); } else if (selecterr != EINTR) { ErrorF("WaitForSomething: select(): errno %d\n", selecterr); } else { /* * must have been broken by a signal. go deal with any * exception flags */ return 0; } } else { /* must have timed out */ ReapAnyOldClients(); LastReapTime = GetTimeInMillis(); } } else { if (AnyClientsWriteBlocked && XFD_ANYSET(&clientsWriteable)) { NewOutputPending = TRUE; XFD_ORSET(&OutputPending, &clientsWriteable, &OutputPending); XFD_UNSET(&ClientsWriteBlocked, &clientsWriteable); if (!XFD_ANYSET(&ClientsWriteBlocked)) AnyClientsWriteBlocked = FALSE; } XFD_ANDSET(&clientsReadable, &LastSelectMask, &AllClients); if (LastSelectMask.fds_bits[0] & WellKnownConnections.fds_bits[0]) MakeNewConnections(); if (XFD_ANYSET(&clientsReadable)) break; } } nready = 0; if (XFD_ANYSET(&clientsReadable)) { ClientPtr client; int conn; if (current_time) /* may not have been set */ current_time = GetTimeInMillis(); for (i = 0; i < howmany(XFD_SETSIZE, NFDBITS); i++) { while (clientsReadable.fds_bits[i]) { curclient = xfd_ffs(clientsReadable.fds_bits[i]) - 1; conn = ConnectionTranslation[curclient + (i * (sizeof(fd_mask) * 8))]; clientsReadable.fds_bits[i] &= ~(((fd_mask)1L) << curclient); client = clients[conn]; if (!client) continue; pClientsReady[nready++] = conn; client->last_request_time = current_time; client->clientGone = CLIENT_ALIVE; if (nready >= MaxClients) { /* pClientsReady buffer has no more room, get the rest on the next time through select() loop */ return nready; } } } } return nready; }
void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) { ASSERT(a_NewMeta <= 8); // Invalid meta values ASSERT(a_NewMeta > 0); // Source blocks aren't spread a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); if ((a_NearChunk == NULL) || (!a_NearChunk->IsValid())) { // Chunk not available return; } const int BlockX = a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX; const int BlockZ = a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ; BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; a_NearChunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); if (IsAllowedBlock(BlockType)) { if ((BlockMeta == a_NewMeta) || IsHigherMeta(BlockMeta, a_NewMeta)) { // Don't spread there, there's already a higher or same level there return; } } // Check water - lava interaction: if (m_FluidBlock == E_BLOCK_LAVA) { if (IsBlockWater(BlockType)) { // Lava flowing into water, change to stone / cobblestone based on direction: BLOCKTYPE NewBlock = (a_NewMeta == 8) ? E_BLOCK_STONE : E_BLOCK_COBBLESTONE; FLOG(" Lava flowing into water, turning water at rel {%d, %d, %d} into stone", a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 8, 0.5f, 1.5f); return; } } else if (m_FluidBlock == E_BLOCK_WATER) { if (IsBlockLava(BlockType)) { // Water flowing into lava, change to cobblestone / obsidian based on dest block: BLOCKTYPE NewBlock = (BlockMeta == 0) ? E_BLOCK_OBSIDIAN : E_BLOCK_COBBLESTONE; FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s", a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); a_NearChunk->BroadcastSoundEffect("random.fizz", BlockX * 8, a_RelY * 8, BlockZ * 8, 0.5f, 1.5f); return; } } else { ASSERT(!"Unknown fluid!"); } if (!IsPassableForFluid(BlockType)) { // Can't spread there return; } // Wash away the block there, if possible: if (CanWashAway(BlockType)) { cBlockHandler * Handler = BlockHandler(BlockType); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockInServerPluginInterface PluginInterface(m_World); Handler->DropBlock( ChunkInterface, m_World, PluginInterface, NULL, BlockX, a_RelY, BlockZ ); } } // if (CanWashAway) // Spread: FLOG(" Spreading to {%d, %d, %d} with meta %d", BlockX, a_RelY, BlockZ, a_NewMeta); a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); m_World.GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, a_NearChunk); HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); }
void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock); a_World->BroadcastSoundEffect("tile.piston.out", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, NULL, a_BlockX, a_BlockY, a_BlockZ); } } // Push blocks, from the furthest to the nearest: int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ; NIBBLETYPE currBlockMeta; std::vector<Vector3i> ScheduledBlocks; ScheduledBlocks.reserve(PISTON_MAX_PUSH_DISTANCE); for (int i = dist + 1; i > 1; i--) { AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta); a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta, false); ScheduledBlocks.push_back(Vector3i(oldx, oldy, oldz)); oldx = a_BlockX; oldy = a_BlockY; oldz = a_BlockZ; } int extx = a_BlockX; int exty = a_BlockY; int extz = a_BlockZ; ScheduledBlocks.push_back(Vector3i(extx, exty, extz)); AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); // "a_Block" now at piston body, "ext" at future extension a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8); a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false); a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks)); }
bool cItemHandler::OnPlayerPlace( cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ ) { if (a_BlockFace < 0) { // Clicked in air return false; } if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) { // The clicked block is outside the world, ignore this call altogether (#128) return false; } BLOCKTYPE ClickedBlock; NIBBLETYPE ClickedBlockMeta; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); // Check if the block ignores build collision (water, grass etc.): if ( BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() || BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta) ) { cChunkInterface ChunkInterface(a_World.GetChunkMap()); BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ); } else { AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) { // The block is being placed outside the world, ignore this packet altogether (#128) return false; } NIBBLETYPE PlaceMeta; BLOCKTYPE PlaceBlock; a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. // No need to do combinability (dblslab) checks, client will do that here. if ( !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() && !BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&a_Player, PlaceMeta) ) { // Tried to place a block *into* another? // Happens when you place a block aiming at side of block with a torch on it or stem beside it return false; } } BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) { // Handler refused the placement, send that information back to the client: a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player); a_Player.GetInventory().SendEquippedSlot(); return false; } if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta)) { // The placement failed, the block has already been re-sent, re-send inventory: a_Player.GetInventory().SendEquippedSlot(); return false; } AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType); float Volume = 1.0f, Pitch = 0.8f; if (PlaceSound == "dig.metal") { Pitch = 1.2f; PlaceSound = "dig.stone"; } else if (PlaceSound == "random.anvil_land") { Volume = 0.65f; } a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch); // Remove the "placed" item: if (a_Player.IsGameModeSurvival()) { a_Player.GetInventory().RemoveOneEquippedItem(); } return true; }
bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) { int BlockX = m_CenterX + static_cast<int>((a_X - m_Width / 2) * GetPixelWidth()); int BlockZ = m_CenterZ + static_cast<int>((a_Z - m_Height / 2) * GetPixelWidth()); int ChunkX, ChunkZ; cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); int RelX = BlockX - (ChunkX * cChunkDef::Width); int RelZ = BlockZ - (ChunkZ * cChunkDef::Width); ASSERT(m_World != nullptr); ColorID PixelData; m_World->DoWithChunk(ChunkX, ChunkZ, [&](cChunk & a_Chunk) { if (!a_Chunk.IsValid()) { return false; } if (GetDimension() == dimNether) { // TODO 2014-02-22 xdot: Nether maps return false; } static const std::array<unsigned char, 4> BrightnessID = { { 3, 0, 1, 2 } }; // Darkest to lightest BLOCKTYPE TargetBlock; NIBBLETYPE TargetMeta; auto Height = a_Chunk.GetHeight(RelX, RelZ); auto ChunkHeight = cChunkDef::Height; a_Chunk.GetBlockTypeMeta(RelX, Height, RelZ, TargetBlock, TargetMeta); auto ColourID = BlockHandler(TargetBlock)->GetMapBaseColourID(TargetMeta); if (IsBlockWater(TargetBlock)) { ChunkHeight /= 4; while (((--Height) != -1) && IsBlockWater(a_Chunk.GetBlock(RelX, Height, RelZ))) { continue; } } else if (ColourID == 0) { while (((--Height) != -1) && ((ColourID = BlockHandler(a_Chunk.GetBlock(RelX, Height, RelZ))->GetMapBaseColourID(a_Chunk.GetMeta(RelX, Height, RelZ))) == 0)) { continue; } } // Multiply base color ID by 4 and add brightness ID const int BrightnessIDSize = static_cast<int>(BrightnessID.size()); PixelData = ColourID * 4 + BrightnessID[static_cast<size_t>(Clamp<int>((BrightnessIDSize * Height) / ChunkHeight, 0, BrightnessIDSize - 1))]; return false; } ); SetPixel(a_X, a_Z, PixelData); return true; }
void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) { BLOCKTYPE pistonBlock; NIBBLETYPE pistonMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta); if (IsExtended(pistonMeta)) { // Already extended, bail out return; } int dist = FirstPassthroughBlock(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, a_World); if (dist < 0) { // FirstPassthroughBlock says piston can't push anything, bail out return; } a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock); a_World->BroadcastSoundEffect("tile.piston.out", static_cast<double>(a_BlockX), static_cast<double>(a_BlockY), static_cast<double>(a_BlockZ), 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block BLOCKTYPE currBlock; NIBBLETYPE currMeta; a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currMeta); if (currBlock != E_BLOCK_AIR) { cBlockHandler * Handler = BlockHandler(currBlock); if (Handler->DoesDropOnUnsuitable()) { cChunkInterface ChunkInterface(a_World->GetChunkMap()); cBlockInServerPluginInterface PluginInterface(*a_World); Handler->DropBlock(ChunkInterface, *a_World, PluginInterface, nullptr, a_BlockX, a_BlockY, a_BlockZ); } } // Push blocks, from the furthest to the nearest: int oldx = a_BlockX, oldy = a_BlockY, oldz = a_BlockZ; NIBBLETYPE currBlockMeta; for (int i = dist + 1; i > 1; i--) { AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, currBlock, currBlockMeta); a_World->SetBlock(oldx, oldy, oldz, currBlock, currBlockMeta); oldx = a_BlockX; oldy = a_BlockY; oldz = a_BlockZ; } int extx = a_BlockX; int exty = a_BlockY; int extz = a_BlockZ; AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); // "a_Block" now at piston body, "ext" at future extension a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8); a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0)); }