diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 738fb1391..fc48c33ed 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -116,6 +116,8 @@ class Level implements ChunkManager, Metadatable{ private static $chunkLoaderCounter = 1; public static $COMPRESSION_LEVEL = 8; + const Y_MASK = 0xFF; + const Y_MAX = 0x100; //256 const BLOCK_UPDATE_NORMAL = 1; const BLOCK_UPDATE_RANDOM = 2; @@ -265,17 +267,17 @@ class Level implements ChunkManager, Metadatable{ } public static function blockHash(int $x, int $y, int $z){ - return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFF) << 35) | (($y & 0x7f) << 28) | ($z & 0xFFFFFFF) : $x . ":" . $y . ":" . $z; + return PHP_INT_SIZE === 8 ? (($x & 0xFFFFFFF) << 36) | (($y & Level::Y_MASK) << 28) | ($z & 0xFFFFFFF) : $x . ":" . $y . ":" . $z; } public static function chunkBlockHash(int $x, int $y, int $z) : int{ - return ($x << 11) | ($z << 7) | $y; + return ($x << 12) | ($z << 8) | $y; } public static function getBlockXYZ($hash, &$x, &$y, &$z){ if(PHP_INT_SIZE === 8){ - $x = ($hash >> 35) << 36 >> 36; - $y = (($hash >> 28) & 0x7f);// << 57 >> 57; //it's always positive + $x = $hash >> 36; + $y = $hash >> 28; //it's always positive $z = ($hash & 0xFFFFFFF) << 36 >> 36; }else{ $hash = explode(":", $hash); @@ -287,7 +289,7 @@ class Level implements ChunkManager, Metadatable{ public static function getXZ($hash, &$x, &$z){ if(PHP_INT_SIZE === 8){ - $x = ($hash >> 32) << 32 >> 32; + $x = $hash >> 32; $z = ($hash & 0xFFFFFFFF) << 32 >> 32; }else{ $hash = explode(":", $hash); @@ -1204,10 +1206,10 @@ class Level implements ChunkManager, Metadatable{ $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); $level = 0; if($chunk !== null){ - $level = $chunk->getBlockSkyLight($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f); + $level = $chunk->getBlockSkyLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); //TODO: decrease light level by time of day if($level < 15){ - $level = max($chunk->getBlockLight($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f)); + $level = max($chunk->getBlockLight($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f)); } } @@ -1222,7 +1224,7 @@ class Level implements ChunkManager, Metadatable{ * @return int bitmap, (id << 4) | data */ public function getFullBlock(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, false)->getFullBlock($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -1238,8 +1240,8 @@ class Level implements ChunkManager, Metadatable{ $index = Level::blockHash($pos->x, $pos->y, $pos->z); if($cached and isset($this->blockCache[$index])){ return $this->blockCache[$index]; - }elseif($pos->y >= 0 and $pos->y < 128 and isset($this->chunks[$chunkIndex = Level::chunkHash($pos->x >> 4, $pos->z >> 4)])){ - $fullState = $this->chunks[$chunkIndex]->getFullBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f); + }elseif($pos->y >= 0 and $pos->y < $this->provider->getWorldHeight() and isset($this->chunks[$chunkIndex = Level::chunkHash($pos->x >> 4, $pos->z >> 4)])){ + $fullState = $this->chunks[$chunkIndex]->getFullBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); }else{ $fullState = 0; } @@ -1370,11 +1372,11 @@ class Level implements ChunkManager, Metadatable{ */ public function setBlock(Vector3 $pos, Block $block, bool $direct = false, bool $update = true) : bool{ $pos = $pos->floor(); - if($pos->y < 0 or $pos->y >= 128){ + if($pos->y < 0 or $pos->y >= $this->provider->getWorldHeight()){ return false; } - if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f, $block->getId(), $block->getDamage())){ + if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f, $block->getId(), $block->getDamage())){ if(!($pos instanceof Position)){ $pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z); } @@ -1611,7 +1613,8 @@ class Level implements ChunkManager, Metadatable{ $target = $this->getBlock($vector); $block = $target->getSide($face); - if($block->y > 127 or $block->y < 0){ + if($block->y >= $this->provider->getWorldHeight() or $block->y < 0){ + //TODO: build height limit messages for custom world heights and mcregion cap return false; } @@ -1859,7 +1862,7 @@ class Level implements ChunkManager, Metadatable{ $chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, false); if($chunk !== null){ - return $chunk->getTile($pos->x & 0x0f, $pos->y & 0xff, $pos->z & 0x0f); + return $chunk->getTile($pos->x & 0x0f, $pos->y & Level::Y_MASK, $pos->z & 0x0f); } return null; @@ -1899,7 +1902,7 @@ class Level implements ChunkManager, Metadatable{ * @return int 0-255 */ public function getBlockIdAt(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -1912,7 +1915,7 @@ class Level implements ChunkManager, Metadatable{ */ public function setBlockIdAt(int $x, int $y, int $z, int $id){ unset($this->blockCache[Level::blockHash($x, $y, $z)]); - $this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); + $this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $id & 0xff); if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){ $this->changedBlocks[$index] = []; @@ -1933,7 +1936,7 @@ class Level implements ChunkManager, Metadatable{ * @return int 16-bit */ public function getBlockExtraDataAt(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -1946,7 +1949,7 @@ class Level implements ChunkManager, Metadatable{ * @param int $data */ public function setBlockExtraDataAt(int $x, int $y, int $z, int $id, int $data){ - $this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & 0x7f, $z & 0x0f, ($data << 8) | $id); + $this->getChunk($x >> 4, $z >> 4, true)->setBlockExtraData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, ($data << 8) | $id); $this->sendBlockExtraData($x, $y, $z, $id, $data); } @@ -1961,7 +1964,7 @@ class Level implements ChunkManager, Metadatable{ * @return int 0-15 */ public function getBlockDataAt(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -1974,7 +1977,7 @@ class Level implements ChunkManager, Metadatable{ */ public function setBlockDataAt(int $x, int $y, int $z, int $data){ unset($this->blockCache[Level::blockHash($x, $y, $z)]); - $this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); + $this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $data & 0x0f); if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){ $this->changedBlocks[$index] = []; @@ -1995,7 +1998,7 @@ class Level implements ChunkManager, Metadatable{ * @return int 0-15 */ public function getBlockSkyLightAt(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -2007,7 +2010,7 @@ class Level implements ChunkManager, Metadatable{ * @param int $level 0-15 */ public function setBlockSkyLightAt(int $x, int $y, int $z, int $level){ - $this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f); + $this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f); } /** @@ -2020,7 +2023,7 @@ class Level implements ChunkManager, Metadatable{ * @return int 0-15 */ public function getBlockLightAt(int $x, int $y, int $z) : int{ - return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f); + return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f); } /** @@ -2032,7 +2035,7 @@ class Level implements ChunkManager, Metadatable{ * @param int $level 0-15 */ public function setBlockLightAt(int $x, int $y, int $z, int $level){ - $this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f); + $this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & Level::Y_MASK, $z & 0x0f, $level & 0x0f); } /** @@ -2205,7 +2208,7 @@ class Level implements ChunkManager, Metadatable{ * @param int $x * @param int $z * - * @return int 0-127 + * @return int 0-255 */ public function getHighestBlockAt(int $x, int $z) : int{ return $this->getChunk($x >> 4, $z >> 4, true)->getHighestBlockAt($x & 0x0f, $z & 0x0f); @@ -2572,12 +2575,13 @@ class Level implements ChunkManager, Metadatable{ $spawn = $this->getSpawnLocation(); } if($spawn instanceof Vector3){ + $max = $this->provider->getWorldHeight(); $v = $spawn->floor(); $chunk = $this->getChunk($v->x >> 4, $v->z >> 4, false); $x = $v->x & 0x0f; $z = $v->z & 0x0f; if($chunk !== null){ - $y = (int) min(126, $v->y); + $y = (int) min($max - 2, $v->y); $wasAir = ($chunk->getBlockId($x, $y - 1, $z) === 0); for(; $y > 0; --$y){ $b = $chunk->getFullBlock($x, $y, $z); @@ -2592,7 +2596,7 @@ class Level implements ChunkManager, Metadatable{ } } - for(; $y >= 0 and $y < 128; ++$y){ + for(; $y >= 0 and $y < $max; ++$y){ $b = $chunk->getFullBlock($x, $y + 1, $z); $block = Block::get($b >> 4, $b & 0x0f); if(!$this->isFullBlock($block)){ diff --git a/src/pocketmine/level/SimpleChunkManager.php b/src/pocketmine/level/SimpleChunkManager.php index 8ffba5c6b..360070824 100644 --- a/src/pocketmine/level/SimpleChunkManager.php +++ b/src/pocketmine/level/SimpleChunkManager.php @@ -47,7 +47,7 @@ class SimpleChunkManager implements ChunkManager{ */ public function getBlockIdAt(int $x, int $y, int $z) : int{ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ - return $chunk->getBlockId($x & 0xf, $y & 0x7f, $z & 0xf); + return $chunk->getBlockId($x & 0xf, $y & Level::Y_MASK, $z & 0xf); } return 0; } @@ -62,7 +62,7 @@ class SimpleChunkManager implements ChunkManager{ */ public function setBlockIdAt(int $x, int $y, int $z, int $id){ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ - $chunk->setBlockId($x & 0xf, $y & 0x7f, $z & 0xf, $id); + $chunk->setBlockId($x & 0xf, $y & Level::Y_MASK, $z & 0xf, $id); } } @@ -77,7 +77,7 @@ class SimpleChunkManager implements ChunkManager{ */ public function getBlockDataAt(int $x, int $y, int $z) : int{ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ - return $chunk->getBlockData($x & 0xf, $y & 0x7f, $z & 0xf); + return $chunk->getBlockData($x & 0xf, $y & Level::Y_MASK, $z & 0xf); } return 0; } @@ -92,7 +92,7 @@ class SimpleChunkManager implements ChunkManager{ */ public function setBlockDataAt(int $x, int $y, int $z, int $data){ if($chunk = $this->getChunk($x >> 4, $z >> 4)){ - $chunk->setBlockData($x & 0xf, $y & 0x7f, $z & 0xf, $data); + $chunk->setBlockData($x & 0xf, $y & Level::Y_MASK, $z & 0xf, $data); } } diff --git a/src/pocketmine/level/format/generic/EmptySubChunk.php b/src/pocketmine/level/format/generic/EmptySubChunk.php index 6046f301e..423a65197 100644 --- a/src/pocketmine/level/format/generic/EmptySubChunk.php +++ b/src/pocketmine/level/format/generic/EmptySubChunk.php @@ -38,16 +38,16 @@ class EmptySubChunk extends SubChunk{ return 0; } - public function setBlockId(int $x, int $y, int $z, int $id){ - + public function setBlockId(int $x, int $y, int $z, int $id) : bool{ + return false; } public function getBlockData(int $x, int $y, int $z) : int{ return 0; } - public function setBlockData(int $x, int $y, int $z, int $data){ - + public function setBlockData(int $x, int $y, int $z, int $data) : bool{ + return false; } public function getFullBlock(int $x, int $y, int $z) : int{ @@ -55,23 +55,23 @@ class EmptySubChunk extends SubChunk{ } public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{ - + return false; } public function getBlockSkyLight(int $x, int $y, int $z) : int{ return 15; } - public function setBlockSkyLight(int $x, int $y, int $z, int $level){ - + public function setBlockSkyLight(int $x, int $y, int $z, int $level) : bool{ + return false; } public function getBlockLight(int $x, int $y, int $z) : int{ return 0; } - public function setBlockLight(int $x, int $y, int $z, int $level){ - + public function setBlockLight(int $x, int $y, int $z, int $level) : bool{ + return false; } public function getBlockIdColumn(int $x, int $z) : string{ diff --git a/src/pocketmine/level/format/generic/GenericChunk.php b/src/pocketmine/level/format/generic/GenericChunk.php index 2e90fd1c1..d9b691ac1 100644 --- a/src/pocketmine/level/format/generic/GenericChunk.php +++ b/src/pocketmine/level/format/generic/GenericChunk.php @@ -164,7 +164,11 @@ class GenericChunk implements Chunk{ } public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null) : bool{ - return $this->getSubChunk($y >> 4, true)->setBlock($x, $y & 0x0f, $z, $blockId !== null ? ($blockId & 0xff) : null, $meta !== null ? ($meta & 0x0f) : null); + if($this->getSubChunk($y >> 4, true)->setBlock($x, $y & 0x0f, $z, $blockId !== null ? ($blockId & 0xff) : null, $meta !== null ? ($meta & 0x0f) : null)){ + $this->hasChanged = true; + return true; + } + return false; } public function getBlockId(int $x, int $y, int $z) : int{ @@ -172,7 +176,9 @@ class GenericChunk implements Chunk{ } public function setBlockId(int $x, int $y, int $z, int $id){ - $this->getSubChunk($y >> 4, true)->setBlockId($x, $y & 0x0f, $z, $id); + if($this->getSubChunk($y >> 4, true)->setBlockId($x, $y & 0x0f, $z, $id)){ + $this->hasChanged = true; + } } public function getBlockData(int $x, int $y, int $z) : int{ @@ -180,7 +186,9 @@ class GenericChunk implements Chunk{ } public function setBlockData(int $x, int $y, int $z, int $data){ - $this->getSubChunk($y >> 4)->setBlockData($x, $y & 0x0f, $z, $data); + if($this->getSubChunk($y >> 4)->setBlockData($x, $y & 0x0f, $z, $data)){ + $this->hasChanged = true; + } } public function getBlockExtraData(int $x, int $y, int $z) : int{ @@ -202,7 +210,9 @@ class GenericChunk implements Chunk{ } public function setBlockSkyLight(int $x, int $y, int $z, int $level){ - $this->getSubChunk($y >> 4)->setBlockSkyLight($x, $y & 0x0f, $z, $level); + if($this->getSubChunk($y >> 4)->setBlockSkyLight($x, $y & 0x0f, $z, $level)){ + $this->hasChanged = true; + } } public function getBlockLight(int $x, int $y, int $z) : int{ @@ -210,7 +220,9 @@ class GenericChunk implements Chunk{ } public function setBlockLight(int $x, int $y, int $z, int $level){ - $this->getSubChunk($y >> 4)->setBlockLight($x, $y & 0x0f, $z, $level); + if($this->getSubChunk($y >> 4)->setBlockLight($x, $y & 0x0f, $z, $level)){ + $this->hasChanged = true; + } } public function getHighestBlockAt(int $x, int $z, bool $useHeightMap = true) : int{ diff --git a/src/pocketmine/level/format/generic/SubChunk.php b/src/pocketmine/level/format/generic/SubChunk.php index dd09f3d81..62e4af0e7 100644 --- a/src/pocketmine/level/format/generic/SubChunk.php +++ b/src/pocketmine/level/format/generic/SubChunk.php @@ -59,8 +59,9 @@ class SubChunk{ return ord($this->ids{($x << 8) | ($z << 4) | $y}); } - public function setBlockId(int $x, int $y, int $z, int $id){ + public function setBlockId(int $x, int $y, int $z, int $id) : bool{ $this->ids{($x << 8) | ($z << 4) | $y} = chr($id); + return true; } public function getBlockData(int $x, int $y, int $z) : int{ @@ -72,15 +73,14 @@ class SubChunk{ } } - public function setBlockData(int $x, int $y, int $z, int $data){ + public function setBlockData(int $x, int $y, int $z, int $data) : bool{ $i = ($x << 7) | ($z << 3) | ($y >> 1); - $current = ord($this->data{$i}); if(($y & 1) === 0){ - $this->data{$i} = chr(($current & 0xf0) | ($data & 0x0f)); + $this->data{$i} = chr((ord($this->data{$i}) & 0xf0) | ($data & 0x0f)); }else{ - $this->data{$i} = chr((($data & 0x0f) << 4) | ($current & 0x0f)); + $this->data{$i} = chr((($data & 0x0f) << 4) | (ord($this->data{$i}) & 0x0f)); } - $this->hasChanged = true; + return true; } public function getFullBlock(int $x, int $y, int $z) : int{ @@ -128,7 +128,7 @@ class SubChunk{ } } - public function setBlockSkyLight(int $x, int $y, int $z, int $level){ + public function setBlockSkyLight(int $x, int $y, int $z, int $level) : bool{ $i = ($x << 7) + ($z << 3) + ($y >> 1); $byte = ord($this->skyLight{$i}); if(($y & 1) === 0){ @@ -136,6 +136,7 @@ class SubChunk{ }else{ $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); } + return true; } public function getBlockLight(int $x, int $y, int $z) : int{ @@ -147,7 +148,7 @@ class SubChunk{ } } - public function setBlockLight(int $x, int $y, int $z, int $level){ + public function setBlockLight(int $x, int $y, int $z, int $level) : bool{ $i = ($x << 7) + ($z << 3) + ($y >> 1); $byte = ord($this->blockLight{$i}); if(($y & 1) === 0){ @@ -155,6 +156,7 @@ class SubChunk{ }else{ $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); } + return true; } public function getHighestBlockAt(int $x, int $z) : int{