isInWorld($x, $y, $z) && ($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){ return BlockChunkReader::getBlock($chunk, $x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK); } return VanillaBlocks::AIR(); } public function setBlockAt(int $x, int $y, int $z, Block $block) : void{ if(($chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE)) !== null){ $xMasked = $x & Chunk::COORD_MASK; $zMasked = $z & Chunk::COORD_MASK; $chunk->setBlockStateId($xMasked, $y, $zMasked, $block->getStateId()); if($block instanceof Waterloggable){ $chunk->setWaterStateId($xMasked, $y, $zMasked, $block->getWaterState()?->getStateId()); } }else{ throw new \InvalidArgumentException("Cannot set block at coordinates x=$x,y=$y,z=$z, terrain is not loaded or out of bounds"); } } public function getChunk(int $chunkX, int $chunkZ) : ?Chunk{ return $this->chunks[World::chunkHash($chunkX, $chunkZ)] ?? null; } public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk) : void{ $this->chunks[World::chunkHash($chunkX, $chunkZ)] = $chunk; } public function cleanChunks() : void{ $this->chunks = []; } public function getMinY() : int{ return $this->minY; } public function getMaxY() : int{ return $this->maxY; } public function isInWorld(int $x, int $y, int $z) : bool{ return ( $x <= Limits::INT32_MAX && $x >= Limits::INT32_MIN && $y < $this->maxY && $y >= $this->minY && $z <= Limits::INT32_MAX && $z >= Limits::INT32_MIN ); } }