diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 005a335b8..9654c7144 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -81,7 +81,7 @@ use pocketmine\inventory\ShapelessRecipe; use pocketmine\inventory\SimpleTransactionGroup; use pocketmine\item\Item; use pocketmine\level\ChunkLoader; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\level\Location; use pocketmine\level\Position; @@ -3540,17 +3540,17 @@ class Player extends Human implements CommandSender, InventoryHolder, ChunkLoade $this->server->getPlayerMetadata()->removeMetadata($this, $metadataKey, $plugin); } - public function onChunkChanged(FullChunk $chunk){ + public function onChunkChanged(Chunk $chunk){ $this->loadQueue[Level::chunkHash($chunk->getX(), $chunk->getZ())] = abs(($this->x >> 4) - $chunk->getX()) + abs(($this->z >> 4) - $chunk->getZ()); } - public function onChunkLoaded(FullChunk $chunk){ + public function onChunkLoaded(Chunk $chunk){ } - public function onChunkPopulated(FullChunk $chunk){ + public function onChunkPopulated(Chunk $chunk){ } - public function onChunkUnloaded(FullChunk $chunk){ + public function onChunkUnloaded(Chunk $chunk){ } public function onBlockChanged(Vector3 $block){ diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index b7ba3b5e2..a27015110 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -21,7 +21,7 @@ namespace pocketmine\entity; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\particle\CriticalParticle; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\protocol\AddEntityPacket; @@ -41,7 +41,7 @@ class Arrow extends Projectile{ protected $isCritical; - public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null, $critical = false){ + public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null, $critical = false){ $this->isCritical = (bool) $critical; parent::__construct($chunk, $nbt, $shootingEntity); } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 813f8ee34..5b359285c 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -35,7 +35,6 @@ use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntityTeleportEvent; use pocketmine\event\Timings; use pocketmine\level\format\Chunk; -use pocketmine\level\format\FullChunk; use pocketmine\level\Level; use pocketmine\level\Location; use pocketmine\level\Position; @@ -255,7 +254,7 @@ abstract class Entity extends Location implements Metadatable{ protected $isPlayer = false; - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ assert($chunk !== null and $chunk->getProvider() !== null); @@ -487,13 +486,13 @@ abstract class Entity extends Location implements Metadatable{ /** * @param int|string $type - * @param FullChunk $chunk + * @param Chunk $chunk * @param CompoundTag $nbt * @param $args * * @return Entity */ - public static function createEntity($type, FullChunk $chunk, CompoundTag $nbt, ...$args){ + public static function createEntity($type, Chunk $chunk, CompoundTag $nbt, ...$args){ if(isset(self::$knownEntities[$type])){ $class = self::$knownEntities[$type]; return new $class($chunk, $nbt, ...$args); diff --git a/src/pocketmine/entity/Projectile.php b/src/pocketmine/entity/Projectile.php index 55a1bdd9e..bf5be20ff 100644 --- a/src/pocketmine/entity/Projectile.php +++ b/src/pocketmine/entity/Projectile.php @@ -26,7 +26,7 @@ use pocketmine\event\entity\EntityDamageByChildEntityEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\ProjectileHitEvent; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\MovingObjectPosition; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; @@ -42,7 +42,7 @@ abstract class Projectile extends Entity{ public $hadCollision = false; - public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){ + public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){ $this->shootingEntity = $shootingEntity; if($shootingEntity !== null){ $this->setDataProperty(self::DATA_SHOOTER_ID, self::DATA_TYPE_LONG, $shootingEntity->getId()); diff --git a/src/pocketmine/entity/Snowball.php b/src/pocketmine/entity/Snowball.php index 40d604068..393beb558 100644 --- a/src/pocketmine/entity/Snowball.php +++ b/src/pocketmine/entity/Snowball.php @@ -21,7 +21,7 @@ namespace pocketmine\entity; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\protocol\AddEntityPacket; use pocketmine\Player; @@ -36,7 +36,7 @@ class Snowball extends Projectile{ protected $gravity = 0.03; protected $drag = 0.01; - public function __construct(FullChunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){ + public function __construct(Chunk $chunk, CompoundTag $nbt, Entity $shootingEntity = null){ parent::__construct($chunk, $nbt, $shootingEntity); } diff --git a/src/pocketmine/event/level/ChunkEvent.php b/src/pocketmine/event/level/ChunkEvent.php index e66dfb88f..15deb6e69 100644 --- a/src/pocketmine/event/level/ChunkEvent.php +++ b/src/pocketmine/event/level/ChunkEvent.php @@ -24,22 +24,22 @@ */ namespace pocketmine\event\level; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; abstract class ChunkEvent extends LevelEvent{ - /** @var FullChunk */ + /** @var Chunk */ private $chunk; /** - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function __construct(FullChunk $chunk){ + public function __construct(Chunk $chunk){ parent::__construct($chunk->getProvider()->getLevel()); $this->chunk = $chunk; } /** - * @return FullChunk + * @return Chunk */ public function getChunk(){ return $this->chunk; diff --git a/src/pocketmine/event/level/ChunkLoadEvent.php b/src/pocketmine/event/level/ChunkLoadEvent.php index cfe99f8bc..a7d0eb6a2 100644 --- a/src/pocketmine/event/level/ChunkLoadEvent.php +++ b/src/pocketmine/event/level/ChunkLoadEvent.php @@ -21,7 +21,7 @@ namespace pocketmine\event\level; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; /** * Called when a Chunk is loaded @@ -31,7 +31,7 @@ class ChunkLoadEvent extends ChunkEvent{ private $newChunk; - public function __construct(FullChunk $chunk, $newChunk){ + public function __construct(Chunk $chunk, $newChunk){ parent::__construct($chunk); $this->newChunk = (bool) $newChunk; } diff --git a/src/pocketmine/item/SpawnEgg.php b/src/pocketmine/item/SpawnEgg.php index a8e56b8fb..a6727625f 100644 --- a/src/pocketmine/item/SpawnEgg.php +++ b/src/pocketmine/item/SpawnEgg.php @@ -24,7 +24,7 @@ namespace pocketmine\item; use pocketmine\block\Block; use pocketmine\entity\Entity; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\Level; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\DoubleTag; @@ -46,7 +46,7 @@ class SpawnEgg extends Item{ $entity = null; $chunk = $level->getChunk($block->getX() >> 4, $block->getZ() >> 4); - if(!($chunk instanceof FullChunk)){ + if(!($chunk instanceof Chunk)){ return false; } diff --git a/src/pocketmine/level/ChunkLoader.php b/src/pocketmine/level/ChunkLoader.php index 77bd9a51f..fb19c5503 100644 --- a/src/pocketmine/level/ChunkLoader.php +++ b/src/pocketmine/level/ChunkLoader.php @@ -22,7 +22,7 @@ namespace pocketmine\level; use pocketmine\block\Block; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\math\Vector3; /** @@ -75,32 +75,32 @@ interface ChunkLoader{ /** * This method will be called when a Chunk is replaced by a new one * - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function onChunkChanged(FullChunk $chunk); + public function onChunkChanged(Chunk $chunk); /** * This method will be called when a registered chunk is loaded * - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function onChunkLoaded(FullChunk $chunk); + public function onChunkLoaded(Chunk $chunk); /** * This method will be called when a registered chunk is unloaded * - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function onChunkUnloaded(FullChunk $chunk); + public function onChunkUnloaded(Chunk $chunk); /** * This method will be called when a registered chunk is populated * Usually it'll be sent with another call to onChunkChanged() * - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function onChunkPopulated(FullChunk $chunk); + public function onChunkPopulated(Chunk $chunk); /** * This method will be called when a block changes in a registered chunk diff --git a/src/pocketmine/level/ChunkManager.php b/src/pocketmine/level/ChunkManager.php index aac28257d..72cd1483d 100644 --- a/src/pocketmine/level/ChunkManager.php +++ b/src/pocketmine/level/ChunkManager.php @@ -23,7 +23,7 @@ declare(strict_types = 1); namespace pocketmine\level; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; interface ChunkManager{ /** @@ -72,16 +72,16 @@ interface ChunkManager{ * @param int $chunkX * @param int $chunkZ * - * @return FullChunk|null + * @return Chunk|null */ public function getChunk(int $chunkX, int $chunkZ); /** - * @param int $chunkX - * @param int $chunkZ - * @param FullChunk $chunk + * @param int $chunkX + * @param int $chunkZ + * @param Chunk $chunk */ - public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null); + public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null); /** * Gets the level seed diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index dbc760589..aa1ec0d72 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -66,7 +66,6 @@ use pocketmine\event\Timings; use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; use pocketmine\level\format\Chunk; -use pocketmine\level\format\FullChunk; use pocketmine\level\format\generic\BaseLevelProvider; use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\format\LevelProvider; @@ -184,7 +183,7 @@ class Level implements ChunkManager, Metadatable{ private $folderName; - /** @var FullChunk[]|Chunk[] */ + /** @var Chunk[] */ private $chunks = []; /** @var Vector3[][] */ @@ -209,7 +208,6 @@ class Level implements ChunkManager, Metadatable{ /** @var BlockMetadataStore */ private $blockMetadata; - private $useSections; private $blockOrder; /** @var Position */ @@ -334,7 +332,6 @@ class Level implements ChunkManager, Metadatable{ $this->generator = Generator::getGenerator($this->provider->getGenerator()); $this->blockOrder = $provider::getProviderOrder(); - $this->useSections = $provider::usesChunkSection(); $this->folderName = $name; $this->updateQueue = new ReversePriorityQueue(); @@ -918,44 +915,20 @@ class Level implements ChunkManager, Metadatable{ } - if($this->useSections){ - foreach($chunk->getSections() as $section){ - if(!($section instanceof EmptyChunkSection)){ - $Y = $section->getY(); - $k = mt_rand(0, 0x7fffffff); - for($i = 0; $i < 3; ++$i, $k >>= 10){ - $x = $k & 0x0f; - $y = ($k >> 8) & 0x0f; - $z = ($k >> 16) & 0x0f; - - $blockId = $section->getBlockId($x, $y, $z); - if(isset($this->randomTickBlocks[$blockId])){ - $class = $this->randomTickBlocks[$blockId]; - /** @var Block $block */ - $block = new $class($section->getBlockData($x, $y, $z)); - $block->x = $chunkX * 16 + $x; - $block->y = ($Y << 4) + $y; - $block->z = $chunkZ * 16 + $z; - $block->level = $this; - $block->onUpdate(self::BLOCK_UPDATE_RANDOM); - } - } - } - } - }else{ - for($Y = 0; $Y < 8 and ($Y < 3 or $blockTest !== 0); ++$Y){ - $blockTest = 0; + foreach($chunk->getSubChunks() as $subChunk){ + if(!$subChunk->isEmpty()){ + $Y = $subChunk->getY(); $k = mt_rand(0, 0x7fffffff); for($i = 0; $i < 3; ++$i, $k >>= 10){ $x = $k & 0x0f; $y = ($k >> 8) & 0x0f; $z = ($k >> 16) & 0x0f; - $blockTest |= $blockId = $chunk->getBlockId($x, $y + ($Y << 4), $z); + $blockId = $subChunk->getBlockId($x, $y, $z); if(isset($this->randomTickBlocks[$blockId])){ $class = $this->randomTickBlocks[$blockId]; /** @var Block $block */ - $block = new $class($chunk->getBlockData($x, $y + ($Y << 4), $z)); + $block = new $class($subChunk->getBlockData($x, $y, $z)); $block->x = $chunkX * 16 + $x; $block->y = ($Y << 4) + $y; $block->z = $chunkZ * 16 + $z; @@ -2122,7 +2095,7 @@ class Level implements ChunkManager, Metadatable{ } /** - * @return FullChunk[]|Chunk[] + * @return Chunk[] */ public function getChunks() : array{ return $this->chunks; @@ -2135,7 +2108,7 @@ class Level implements ChunkManager, Metadatable{ * @param int $z * @param bool $create Whether to generate the chunk if it does not exist * - * @return FullChunk|Chunk + * @return Chunk */ public function getChunk(int $x, int $z, bool $create = false){ if(isset($this->chunks[$index = Level::chunkHash($x, $z)])){ @@ -2147,7 +2120,7 @@ class Level implements ChunkManager, Metadatable{ return null; } - public function generateChunkCallback(int $x, int $z, FullChunk $chunk){ + public function generateChunkCallback(int $x, int $z, Chunk $chunk){ Timings::$generationCallbackTimer->startTiming(); if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){ $oldChunk = $this->getChunk($x, $z, false); @@ -2182,10 +2155,10 @@ class Level implements ChunkManager, Metadatable{ /** * @param int $chunkX * @param int $chunkZ - * @param FullChunk $chunk + * @param Chunk $chunk * @param bool $unload */ - public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null, bool $unload = true){ + public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null, bool $unload = true){ if($chunk === null){ return; } diff --git a/src/pocketmine/level/SimpleChunkManager.php b/src/pocketmine/level/SimpleChunkManager.php index 4f0d5b8ec..a117c7cb7 100644 --- a/src/pocketmine/level/SimpleChunkManager.php +++ b/src/pocketmine/level/SimpleChunkManager.php @@ -23,11 +23,11 @@ declare(strict_types = 1); namespace pocketmine\level; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; class SimpleChunkManager implements ChunkManager{ - /** @var FullChunk[] */ + /** @var Chunk[] */ protected $chunks = []; protected $seed; @@ -100,7 +100,7 @@ class SimpleChunkManager implements ChunkManager{ * @param int $chunkX * @param int $chunkZ * - * @return FullChunk|null + * @return Chunk|null */ public function getChunk(int $chunkX, int $chunkZ){ return isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunks[$index] : null; @@ -109,9 +109,9 @@ class SimpleChunkManager implements ChunkManager{ /** * @param int $chunkX * @param int $chunkZ - * @param FullChunk $chunk + * @param Chunk $chunk */ - public function setChunk(int $chunkX, int $chunkZ, FullChunk $chunk = null){ + public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk = null){ if($chunk === null){ unset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); return; diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 6a9959410..48716fd86 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -21,36 +21,400 @@ namespace pocketmine\level\format; -interface Chunk extends FullChunk{ - const SECTION_COUNT = 8; +use pocketmine\entity\Entity; +use pocketmine\level\format\generic\SubChunk; +use pocketmine\tile\Tile; + +interface Chunk{ + const MAX_SUBCHUNKS = 16; + + const DATA_ORDER_XZY = 0; + const DATA_ORDER_YZX = 1; /** - * Tests whether a section (mini-chunk) is empty + * @return int + */ + public function getX() : int; + + /** + * @return int + */ + public function getZ() : int; + + public function setX(int $x); + + public function setZ(int $z); + + /** + * @return LevelProvider|null + */ + public function getProvider(); + + /** + * @param LevelProvider $provider + */ + public function setProvider(LevelProvider $provider); + + /** + * Returns the chunk height in subchunks * - * @param $fY 0-7, (Y / 16) + * @return int + */ + public function getHeight() : int; + /** + * Gets block and meta in one go + * + * @param int $x 0-15 + * @param int $y 0-15 + * @param int $z 0-15 + * + * @return int bitmap, (id << 4) | data + */ + public function getFullBlock(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $blockId , if null, do not change + * @param int $meta 0-15, if null, do not change + * + */ + public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null); + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * + * @return int 0-255 + */ + public function getBlockId(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $id 0-255 + */ + public function setBlockId(int $x, int $y, int $z, int $id); + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * + * @return int 0-15 + */ + public function getBlockData(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $data 0-15 + */ + public function setBlockData(int $x, int $y, int $z, int $data); + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * + * @return int (16-bit) + */ + public function getBlockExtraData(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $data (16-bit) + */ + public function setBlockExtraData(int $x, int $y, int $z, int $data); + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * + * @return int 0-15 + */ + public function getBlockSkyLight(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $level 0-15 + */ + public function setBlockSkyLight(int $x, int $y, int $z, int $level); + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * + * @return int 0-15 + */ + public function getBlockLight(int $x, int $y, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + * @param int $level 0-15 + */ + public function setBlockLight(int $x, int $y, int $z, int $level); + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * + * @return int 0-255 + */ + public function getHighestBlockAt(int $x, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * + * @return int 0-255 + */ + public function getHeightMap(int $x, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * @param $value 0-255 + */ + public function setHeightMap(int $x, int $z, int $value); + + public function recalculateHeightMap(); + + public function populateSkyLight(); + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * + * @return int 0-255 + */ + public function getBiomeId(int $x, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * @param int $biomeId 0-255 + */ + public function setBiomeId(int $x, int $z, int $biomeId); + + /** + * @param int $x + * @param int $z + * + * @return int[] RGB bytes + */ + public function getBiomeColor(int $x, int $z) : int; + + /** + * @param int $x 0-15 + * @param int $z 0-15 + * @param int $R 0-255 + * @param int $G 0-255 + * @param int $B 0-255 + */ + public function setBiomeColor(int $x, int $z, int $R, int $G, int $B); + + public function getBlockIdColumn(int $x, int $z) : string; + + public function getBlockDataColumn(int $x, int $z) : string; + + public function getBlockSkyLightColumn(int $x, int $z) : string; + + public function getBlockLightColumn(int $x, int $z) : string; + + public function isLightPopulated() : bool; + + public function setLightPopulated(bool $value = true); + + public function isPopulated() : bool; + + public function setPopulated(bool $value = true); + + public function isGenerated() : bool; + + public function setGenerated(bool $value = true); + + /** + * @param Entity $entity + */ + public function addEntity(Entity $entity); + + /** + * @param Entity $entity + */ + public function removeEntity(Entity $entity); + + /** + * @param Tile $tile + */ + public function addTile(Tile $tile); + + /** + * @param Tile $tile + */ + public function removeTile(Tile $tile); + + /** + * @return Entity[] + */ + public function getEntities() : array; + + /** + * @return Tile[] + */ + public function getTiles() : array; + + /** + * @param int $x 0-15 + * @param int $y 0-255 + * @param int $z 0-15 + */ + public function getTile(int $x, int $y, int $z); + + /** + * @return bool + */ + public function isLoaded() : bool; + + /** + * Loads the chunk + * + * @param bool $generate If the chunk does not exist, generate it * * @return bool */ - public function isSectionEmpty($fY); + public function load(bool $generate = true) : bool; /** - * @param int $fY 0-7 + * @param bool $save + * @param bool $safe If false, unload the chunk even if players are nearby * - * @return ChunkSection + * @return bool */ - public function getSection($fY); + public function unload(bool $save = true, bool $safe = true) : bool; + + public function initChunk(); /** - * @param int $fY 0-7 - * @param ChunkSection $section + * @return string + */ + public function getBiomeIdArray() : string; + + /** + * @return int[] + */ + public function getBiomeColorArray() : array; + + /** + * @return int[] + */ + public function getHeightMapArray() : array; + + public function getBlockIdArray() : string; + + public function getBlockDataArray() : string; + + public function getBlockExtraDataArray() : array; + + public function getBlockSkyLightArray() : string; + + public function getBlockLightArray() : string; + + /** + * @return bool + */ + public function hasChanged() : bool; + + /** + * @param bool $changed + */ + public function setChanged(bool $changed = true); + + /** + * @param int $fY 0-15 + * @param bool $generateNew will set and return a modifiable subchunk * - * @return boolean + * @return SubChunk */ - public function setSection($fY, ChunkSection $section); + public function getSubChunk(int $fY, bool $generateNew = false) : SubChunk; + + /** + * @param int $fY 0-15 + * @param SubChunk $subChunk + * + * @return bool + */ + public function setSubChunk(int $fY, SubChunk $subChunk = null, bool $allowEmpty = false) : bool; /** - * @return ChunkSection[] + * @return SubChunk[] */ - public function getSections(); + public function getSubChunks() : array; + + /** + * Returns the index of the highest non-empty subchunk + * + * @return bool + */ + public function getHighestSubChunkIndex() : int; + + /** + * Returns the number of subchunks that need sending + * + * @return int + */ + public function getSubChunkSendCount() : int; + + /** + * Disposes of empty subchunks + */ + public function clearEmptySubChunks(); + + /** + * Serializes the chunk to network data + * + * @return string + */ + public function networkSerialize() : string; + + /** + * Serializes a chunk without compression for use in AsyncTasks. + * + * @return string + */ + public static function fastSerialize(Chunk $chunk) : string; + + /** + * Deserializes a chunk from fast serialization + * + * @param string $data + * @param LevelProvider $provider + * + * @return Chunk|null + */ + public static function fastDeserialize(string $data, LevelProvider $provider = null); + + /** + * Creates and returns an empty chunk + * + * @param int $chunkX + * @param int $chunkZ + * @param LevelProvider $provider + * + * @return Chunk + */ + public static function getEmptyChunk(int $chunkX, int $chunkZ, LevelProvider $provider = null) : Chunk; } \ No newline at end of file diff --git a/src/pocketmine/level/format/ChunkSection.php b/src/pocketmine/level/format/ChunkSection.php deleted file mode 100644 index 130032bc0..000000000 --- a/src/pocketmine/level/format/ChunkSection.php +++ /dev/null @@ -1,169 +0,0 @@ -xPos = new IntTag("xPos", $chunk->getX()); + $nbt->zPos = new IntTag("zPos", $chunk->getZ()); + + $nbt->V = new ByteTag("V", 1); + $nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO + $nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO + $nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $chunk->isGenerated()); + $nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated()); + $nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated()); + + $nbt->Sections = new ListTag("Sections", []); + $nbt->Sections->setTagType(NBT::TAG_Compound); + $subChunks = -1; + foreach($chunk->getSubChunks() as $subChunk){ + if($subChunk->isEmpty()){ + continue; + } + $nbt->Sections[++$subChunks] = new CompoundTag(null, [ + "Y" => new ByteTag("Y", $subChunk->getY()), + "Blocks" => new ByteArrayTag("Blocks", GenericChunk::reorderByteArray($subChunk->getBlockIdArray())), //Generic in-memory chunks are currrently always XZY + "Data" => new ByteArrayTag("Data", GenericChunk::reorderNibbleArray($subChunk->getBlockDataArray())), + "BlockLight" => new ByteArrayTag("BlockLight", GenericChunk::reorderNibbleArray($subChunk->getBlockLightArray())), + "SkyLight" => new ByteArrayTag("SkyLight", GenericChunk::reorderNibbleArray($subChunk->getSkyLightArray())) + ]); + } + + $nbt->BiomeColors = new IntArrayTag("BiomeColors", $chunk->getBiomeColorArray()); + $nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); + + $entities = []; + + foreach($chunk->getEntities() as $entity){ + if(!($entity instanceof Player) and !$entity->closed){ + $entity->saveNBT(); + $entities[] = $entity->namedtag; + } + } + + $nbt->Entities = new ListTag("Entities", $entities); + $nbt->Entities->setTagType(NBT::TAG_Compound); + + $tiles = []; + foreach($chunk->getTiles() as $tile){ + $tile->saveNBT(); + $tiles[] = $tile->namedtag; + } + + $nbt->TileEntities = new ListTag("TileEntities", $tiles); + $nbt->TileEntities->setTagType(NBT::TAG_Compound); + + //TODO: TileTicks + + $writer = new NBT(NBT::BIG_ENDIAN); + $nbt->setName("Level"); + $writer->setData(new CompoundTag("", ["Level" => $nbt])); + + return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); + } + + public static function nbtDeserialize(string $data, LevelProvider $provider = null){ + $nbt = new NBT(NBT::BIG_ENDIAN); + try{ + $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); + + $chunk = $nbt->getData(); + + if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ + throw new ChunkException("Invalid NBT format"); + } + + $chunk = $chunk->Level; + + $subChunks = []; + if($chunk->Sections instanceof ListTag){ + foreach($chunk->Sections as $subChunk){ + if($subChunk instanceof CompoundTag){ + $subChunks[] = new SubChunk( + $subChunk->Y->getValue(), + GenericChunk::reorderByteArray($subChunk->Blocks->getValue()), + GenericChunk::reorderNibbleArray($subChunk->Data->getValue()), + GenericChunk::reorderNibbleArray($subChunk->BlockLight->getValue()), + GenericChunk::reorderNibbleArray($subChunk->SkyLight->getValue()) + ); + } + } + } + + $result = new GenericChunk( + $provider, + $chunk["xPos"], + $chunk["zPos"], + $subChunks, + $chunk->Entities instanceof ListTag ? $chunk->Entities->getValue() : [], + $chunk->TileEntities instanceof ListTag ? $chunk->TileEntities->getValue() : [], + $chunk->BiomeColors instanceof IntArrayTag ? $chunk->BiomeColors->getValue() : [], + $chunk->HeightMap instanceof IntArrayTag ? $chunk->HeightMap->getValue() : [] + ); + $result->setLightPopulated($chunk->LightPopulated instanceof ByteTag ? ((bool) $chunk->LightPopulated->getValue()) : false); + $result->setPopulated($chunk->TerrainPopulated instanceof ByteTag ? ((bool) $chunk->TerrainPopulated->getValue()) : false); + $result->setGenerated($chunk->TerrainGenerated instanceof ByteTag ? ((bool) $chunk->TerrainGenerated->getValue()) : false); + return $result; + }catch(\Throwable $e){ + echo $e->getMessage(); + return null; + } + } + /** @var RegionLoader[] */ protected $regions = []; - /** @var Chunk[] */ + /** @var AnvilChunk[] */ protected $chunks = []; public static function getProviderName(){ @@ -49,10 +159,6 @@ class Anvil extends McRegion{ return self::ORDER_YZX; } - public static function usesChunkSection(){ - return true; - } - public static function isValid($path){ $isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/")); @@ -69,45 +175,9 @@ class Anvil extends McRegion{ return $isValid; } - public function requestChunkTask($x, $z){ - $chunk = $this->getChunk($x, $z, false); - if(!($chunk instanceof Chunk)){ - throw new ChunkException("Invalid Chunk sent"); - } - - $tiles = ""; - - if(count($chunk->getTiles()) > 0){ - $nbt = new NBT(NBT::LITTLE_ENDIAN); - $list = []; - foreach($chunk->getTiles() as $tile){ - if($tile instanceof Spawnable){ - $list[] = $tile->getSpawnCompound(); - } - } - $nbt->setData($list); - $tiles = $nbt->write(true); - } - - $extraData = new BinaryStream(); - $extraData->putLInt(count($chunk->getBlockExtraDataArray())); - foreach($chunk->getBlockExtraDataArray() as $key => $value){ - $extraData->putLInt($key); - $extraData->putLShort($value); - } - - $ordered = $chunk->getBlockIdArray() . - $chunk->getBlockDataArray() . - $chunk->getBlockSkyLightArray() . - $chunk->getBlockLightArray() . - pack("C*", ...$chunk->getHeightMapArray()) . - pack("N*", ...$chunk->getBiomeColorArray()) . - $extraData->getBuffer() . - $tiles; - - $this->getLevel()->chunkRequestCallback($x, $z, $ordered); - - return null; + public function getWorldHeight() : int{ + //TODO: add world height options + return 256; } /** @@ -117,55 +187,7 @@ class Anvil extends McRegion{ * @return RegionLoader */ protected function getRegion($x, $z){ - return isset($this->regions[$index = Level::chunkHash($x, $z)]) ? $this->regions[$index] : null; - } - - /** - * @param int $chunkX - * @param int $chunkZ - * @param bool $create - * - * @return Chunk - */ - public function getChunk($chunkX, $chunkZ, $create = false){ - return parent::getChunk($chunkX, $chunkZ, $create); - } - - public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ - if(!($chunk instanceof Chunk)){ - throw new ChunkException("Invalid Chunk class"); - } - - $chunk->setProvider($this); - - self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); - $this->loadRegion($regionX, $regionZ); - - $chunk->setX($chunkX); - $chunk->setZ($chunkZ); - $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; - } - - public function getEmptyChunk($chunkX, $chunkZ){ - return Chunk::getEmptyChunk($chunkX, $chunkZ, $this); - } - - public static function createChunkSection($Y){ - return new ChunkSection(new CompoundTag("", [ - "Y" => new ByteTag("Y", $Y), - "Blocks" => new ByteArrayTag("Blocks", str_repeat("\x00", 4096)), - "Data" => new ByteArrayTag("Data", str_repeat("\x00", 2048)), - "SkyLight" => new ByteArrayTag("SkyLight", str_repeat("\xff", 2048)), - "BlockLight" => new ByteArrayTag("BlockLight", str_repeat("\x00", 2048)) - ])); - } - - public function isChunkGenerated($chunkX, $chunkZ){ - if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){ - return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated(); - } - - return false; + return $this->regions[Level::chunkHash($x, $z)] ?? null; } protected function loadRegion($x, $z){ diff --git a/src/pocketmine/level/format/anvil/Chunk.php b/src/pocketmine/level/format/anvil/Chunk.php deleted file mode 100644 index 0f30687d2..000000000 --- a/src/pocketmine/level/format/anvil/Chunk.php +++ /dev/null @@ -1,368 +0,0 @@ -provider = $level; - $this->nbt = new CompoundTag("Level", []); - return; - } - - $this->nbt = $nbt; - - if(!isset($this->nbt->Entities) or !($this->nbt->Entities instanceof ListTag)){ - $this->nbt->Entities = new ListTag("Entities", []); - $this->nbt->Entities->setTagType(NBT::TAG_Compound); - } - - if(!isset($this->nbt->TileEntities) or !($this->nbt->TileEntities instanceof ListTag)){ - $this->nbt->TileEntities = new ListTag("TileEntities", []); - $this->nbt->TileEntities->setTagType(NBT::TAG_Compound); - } - - if(!isset($this->nbt->TileTicks) or !($this->nbt->TileTicks instanceof ListTag)){ - $this->nbt->TileTicks = new ListTag("TileTicks", []); - $this->nbt->TileTicks->setTagType(NBT::TAG_Compound); - } - - if(!isset($this->nbt->Sections) or !($this->nbt->Sections instanceof ListTag)){ - $this->nbt->Sections = new ListTag("Sections", []); - $this->nbt->Sections->setTagType(NBT::TAG_Compound); - } - - if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArrayTag)){ - $this->nbt->BiomeColors = new IntArrayTag("BiomeColors", array_fill(0, 256, 0)); - } - - if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArrayTag)){ - $this->nbt->HeightMap = new IntArrayTag("HeightMap", array_fill(0, 256, 0)); - } - - $sections = []; - foreach($this->nbt->Sections as $section){ - if($section instanceof CompoundTag){ - $y = (int) $section["Y"]; - if($y < 8){ - $sections[$y] = new ChunkSection($section); - } - } - } - for($y = 0; $y < 8; ++$y){ - if(!isset($sections[$y])){ - $sections[$y] = new EmptyChunkSection($y); - } - } - - $extraData = []; - - if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArrayTag)){ - $this->nbt->ExtraData = new ByteArrayTag("ExtraData", Binary::writeInt(0)); - }else{ - $stream = new BinaryStream($this->nbt->ExtraData->getValue()); - $count = $stream->getInt(); - for($i = 0; $i < $count; ++$i){ - $key = $stream->getInt(); - $extraData[$key] = $stream->getShort(false); - } - } - - parent::__construct($level, (int) $this->nbt["xPos"], (int) $this->nbt["zPos"], $sections, $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData); - - if(isset($this->nbt->Biomes)){ - $this->checkOldBiomes($this->nbt->Biomes->getValue()); - unset($this->nbt->Biomes); - } - - unset($this->nbt->Sections, $this->nbt->ExtraData); - } - - public function isLightPopulated(){ - return $this->nbt["LightPopulated"] > 0; - } - - public function setLightPopulated($value = 1){ - $this->nbt->LightPopulated = new ByteTag("LightPopulated", $value); - $this->hasChanged = true; - } - - /** - * @return bool - */ - public function isPopulated(){ - return $this->nbt["TerrainPopulated"] > 0; - } - - /** - * @param int $value - */ - public function setPopulated($value = 1){ - $this->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $value); - $this->hasChanged = true; - } - - /** - * @return bool - */ - public function isGenerated(){ - return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0); - } - - /** - * @param int $value - */ - public function setGenerated($value = 1){ - $this->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $value); - $this->hasChanged = true; - } - - /** - * @return CompoundTag - */ - public function getNBT(){ - return $this->nbt; - } - - /** - * @param string $data - * @param LevelProvider $provider - * - * @return Chunk - */ - public static function fromBinary($data, LevelProvider $provider = null){ - $nbt = new NBT(NBT::BIG_ENDIAN); - - try{ - $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); - $chunk = $nbt->getData(); - - if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ - return null; - } - - return new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, $chunk->Level); - }catch(\Throwable $e){ - return null; - } - } - - /** - * @param string $data - * @param LevelProvider $provider - * - * @return Chunk - */ - public static function fromFastBinary($data, LevelProvider $provider = null){ - $nbt = new NBT(NBT::BIG_ENDIAN); - - try{ - $nbt->read($data); - $chunk = $nbt->getData(); - - if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ - return null; - } - - return new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, $chunk->Level); - }catch(\Throwable $e){ - return null; - } - } - - public function toFastBinary(){ - $nbt = clone $this->getNBT(); - - $nbt->xPos = new IntTag("xPos", $this->x); - $nbt->zPos = new IntTag("zPos", $this->z); - - $nbt->Sections = new ListTag("Sections", []); - $nbt->Sections->setTagType(NBT::TAG_Compound); - foreach($this->getSections() as $section){ - if($section instanceof EmptyChunkSection){ - continue; - } - $nbt->Sections[$section->getY()] = new CompoundTag(null, [ - "Y" => new ByteTag("Y", $section->getY()), - "Blocks" => new ByteArrayTag("Blocks", $section->getIdArray()), - "Data" => new ByteArrayTag("Data", $section->getDataArray()), - "BlockLight" => new ByteArrayTag("BlockLight", $section->getLightArray()), - "SkyLight" => new ByteArrayTag("SkyLight", $section->getSkyLightArray()) - ]); - } - - $nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray()); - - $nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray()); - - $entities = []; - - foreach($this->getEntities() as $entity){ - if(!($entity instanceof Player) and !$entity->closed){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new ListTag("Entities", $entities); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - - $tiles = []; - foreach($this->getTiles() as $tile){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - - $nbt->TileEntities = new ListTag("TileEntities", $tiles); - $nbt->TileEntities->setTagType(NBT::TAG_Compound); - - $extraData = new BinaryStream(); - $extraData->putInt(count($this->getBlockExtraDataArray())); - foreach($this->getBlockExtraDataArray() as $key => $value){ - $extraData->putInt($key); - $extraData->putShort($value); - } - - $nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer()); - - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new CompoundTag("", ["Level" => $nbt])); - - return $writer->write(); - } - - public function toBinary(){ - $nbt = clone $this->getNBT(); - - $nbt->xPos = new IntTag("xPos", $this->x); - $nbt->zPos = new IntTag("zPos", $this->z); - - $nbt->Sections = new ListTag("Sections", []); - $nbt->Sections->setTagType(NBT::TAG_Compound); - foreach($this->getSections() as $section){ - if($section instanceof EmptyChunkSection){ - continue; - } - $nbt->Sections[$section->getY()] = new CompoundTag(null, [ - "Y" => new ByteTag("Y", $section->getY()), - "Blocks" => new ByteArrayTag("Blocks", $section->getIdArray()), - "Data" => new ByteArrayTag("Data", $section->getDataArray()), - "BlockLight" => new ByteArrayTag("BlockLight", $section->getLightArray()), - "SkyLight" => new ByteArrayTag("SkyLight", $section->getSkyLightArray()) - ]); - } - - $nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray()); - - $nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray()); - - $entities = []; - - foreach($this->getEntities() as $entity){ - if(!($entity instanceof Player) and !$entity->closed){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new ListTag("Entities", $entities); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - - $tiles = []; - foreach($this->getTiles() as $tile){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - - $nbt->TileEntities = new ListTag("TileEntities", $tiles); - $nbt->TileEntities->setTagType(NBT::TAG_Compound); - - $extraData = new BinaryStream(); - $extraData->putInt(count($this->getBlockExtraDataArray())); - foreach($this->getBlockExtraDataArray() as $key => $value){ - $extraData->putInt($key); - $extraData->putShort($value); - } - - $nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer()); - - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new CompoundTag("", ["Level" => $nbt])); - - return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); - } - - /** - * @param int $chunkX - * @param int $chunkZ - * @param LevelProvider $provider - * - * @return Chunk - */ - public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){ - try{ - $chunk = new Chunk($provider instanceof LevelProvider ? $provider : Anvil::class, null); - $chunk->x = $chunkX; - $chunk->z = $chunkZ; - - for($y = 0; $y < 8; ++$y){ - $chunk->sections[$y] = new EmptyChunkSection($y); - } - - $chunk->heightMap = array_fill(0, 256, 0); - $chunk->biomeColors = array_fill(0, 256, 0); - - $chunk->nbt->V = new ByteTag("V", 1); - $chunk->nbt->InhabitedTime = new LongTag("InhabitedTime", 0); - $chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", 0); - $chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", 0); - $chunk->nbt->LightPopulated = new ByteTag("LightPopulated", 0); - - return $chunk; - }catch(\Throwable $e){ - return null; - } - } -} \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/ChunkSection.php b/src/pocketmine/level/format/anvil/ChunkSection.php deleted file mode 100644 index 872fab017..000000000 --- a/src/pocketmine/level/format/anvil/ChunkSection.php +++ /dev/null @@ -1,226 +0,0 @@ -y = (int) $nbt["Y"]; - $this->blocks = (string) $nbt["Blocks"]; - $this->data = (string) $nbt["Data"]; - $this->blockLight = (string) $nbt["BlockLight"]; - $this->skyLight = (string) $nbt["SkyLight"]; - } - - public function getY(){ - return $this->y; - } - - public function getBlockId($x, $y, $z){ - return ord($this->blocks{($y << 8) + ($z << 4) + $x}); - } - - public function setBlockId($x, $y, $z, $id){ - $this->blocks{($y << 8) + ($z << 4) + $x} = chr($id); - } - - public function getBlockData($x, $y, $z){ - $m = ord($this->data{($y << 7) + ($z << 3) + ($x >> 1)}); - if(($x & 1) === 0){ - return $m & 0x0F; - }else{ - return $m >> 4; - } - } - - public function setBlockData($x, $y, $z, $data){ - $i = ($y << 7) + ($z << 3) + ($x >> 1); - $old_m = ord($this->data{$i}); - if(($x & 1) === 0){ - $this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f)); - }else{ - $this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f)); - } - } - - public function getFullBlock($x, $y, $z){ - $i = ($y << 8) + ($z << 4) + $x; - if(($x & 1) === 0){ - return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0F); - }else{ - return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4); - } - } - - public function setBlock($x, $y, $z, $blockId = null, $meta = null){ - $i = ($y << 8) + ($z << 4) + $x; - - $changed = false; - - if($blockId !== null){ - $blockId = chr($blockId); - if($this->blocks{$i} !== $blockId){ - $this->blocks{$i} = $blockId; - $changed = true; - } - } - - if($meta !== null){ - $i >>= 1; - $old_m = ord($this->data{$i}); - if(($x & 1) === 0){ - $this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f)); - if(($old_m & 0x0f) !== $meta){ - $changed = true; - } - }else{ - $this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f)); - if((($old_m & 0xf0) >> 4) !== $meta){ - $changed = true; - } - } - } - - return $changed; - } - - public function getBlockSkyLight($x, $y, $z){ - $sl = ord($this->skyLight{($y << 7) + ($z << 3) + ($x >> 1)}); - if(($x & 1) === 0){ - return $sl & 0x0F; - }else{ - return $sl >> 4; - } - } - - public function setBlockSkyLight($x, $y, $z, $level){ - $i = ($y << 7) + ($z << 3) + ($x >> 1); - $old_sl = ord($this->skyLight{$i}); - if(($x & 1) === 0){ - $this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f)); - }else{ - $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f)); - } - } - - public function getBlockLight($x, $y, $z){ - $l = ord($this->blockLight{($y << 7) + ($z << 3) + ($x >> 1)}); - if(($x & 1) === 0){ - return $l & 0x0F; - }else{ - return $l >> 4; - } - } - - public function setBlockLight($x, $y, $z, $level){ - $i = ($y << 7) + ($z << 3) + ($x >> 1); - $old_l = ord($this->blockLight{$i}); - if(($x & 1) === 0){ - $this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f)); - }else{ - $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f)); - } - } - - public function getBlockIdColumn($x, $z){ - $i = ($z << 4) + $x; - $column = ""; - for($y = 0; $y < 16; ++$y){ - $column .= $this->blocks{($y << 8) + $i}; - } - - return $column; - } - - public function getBlockDataColumn($x, $z){ - $i = ($z << 3) + ($x >> 1); - $column = ""; - if(($x & 1) === 0){ - for($y = 0; $y < 16; $y += 2){ - $column .= ($this->data{($y << 7) + $i} & "\x0f") | chr((ord($this->data{(($y + 1) << 7) + $i}) & 0x0f) << 4); - } - }else{ - for($y = 0; $y < 16; $y += 2){ - $column .= chr((ord($this->data{($y << 7) + $i}) & 0xf0) >> 4) | ($this->data{(($y + 1) << 7) + $i} & "\xf0"); - } - } - - return $column; - } - - public function getBlockSkyLightColumn($x, $z){ - $i = ($z << 3) + ($x >> 1); - $column = ""; - if(($x & 1) === 0){ - for($y = 0; $y < 16; $y += 2){ - $column .= ($this->skyLight{($y << 7) + $i} & "\x0f") | chr((ord($this->skyLight{(($y + 1) << 7) + $i}) & 0x0f) << 4); - } - }else{ - for($y = 0; $y < 16; $y += 2){ - $column .= chr((ord($this->skyLight{($y << 7) + $i}) & 0xf0) >> 4) | ($this->skyLight{(($y + 1) << 7) + $i} & "\xf0"); - } - } - - return $column; - } - - public function getBlockLightColumn($x, $z){ - $i = ($z << 3) + ($x >> 1); - $column = ""; - if(($x & 1) === 0){ - for($y = 0; $y < 16; $y += 2){ - $column .= ($this->blockLight{($y << 7) + $i} & "\x0f") | chr((ord($this->blockLight{(($y + 1) << 7) + $i}) & 0x0f) << 4); - } - }else{ - for($y = 0; $y < 16; $y += 2){ - $column .= chr((ord($this->blockLight{($y << 7) + $i}) & 0xf0) >> 4) | ($this->blockLight{(($y + 1) << 7) + $i} & "\xf0"); - } - } - - return $column; - } - - public function getIdArray(){ - return $this->blocks; - } - - public function getDataArray(){ - return $this->data; - } - - public function getSkyLightArray(){ - return $this->skyLight; - } - - public function getLightArray(){ - return $this->blockLight; - } - -} \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/RegionLoader.php b/src/pocketmine/level/format/anvil/RegionLoader.php index 7897474ba..9e4719302 100644 --- a/src/pocketmine/level/format/anvil/RegionLoader.php +++ b/src/pocketmine/level/format/anvil/RegionLoader.php @@ -22,6 +22,7 @@ namespace pocketmine\level\format\anvil; use pocketmine\level\format\LevelProvider; +use pocketmine\level\format\generic\GenericChunk; class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ @@ -45,6 +46,14 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ } protected function unserializeChunk($data){ - return Chunk::fromBinary($data, $this->levelProvider); + return Anvil::nbtDeserialize($data, $this->levelProvider); + } + + public function writeChunk(GenericChunk $chunk){ + $this->lastUsed = time(); + $chunkData = Anvil::nbtSerialize($chunk); + if($chunkData !== false){ + $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData); + } } } \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/BaseChunk.php b/src/pocketmine/level/format/generic/BaseChunk.php deleted file mode 100644 index 5cdc24ef9..000000000 --- a/src/pocketmine/level/format/generic/BaseChunk.php +++ /dev/null @@ -1,260 +0,0 @@ -provider = $provider; - $this->x = (int) $x; - $this->z = (int) $z; - foreach($sections as $Y => $section){ - if($section instanceof ChunkSection){ - $this->sections[$Y] = $section; - }else{ - throw new ChunkException("Received invalid ChunkSection instance"); - } - - if($Y >= self::SECTION_COUNT){ - throw new ChunkException("Invalid amount of chunks"); - } - } - - if(count($biomeColors) === 256){ - $this->biomeColors = $biomeColors; - }else{ - $this->biomeColors = array_fill(0, 256, Binary::readInt("\xff\x00\x00\x00")); - } - - if(count($heightMap) === 256){ - $this->heightMap = $heightMap; - }else{ - $this->heightMap = array_fill(0, 256, 127); - } - - $this->NBTtiles = $tiles; - $this->NBTentities = $entities; - } - - public function getFullBlock($x, $y, $z){ - return $this->sections[$y >> 4]->getFullBlock($x, $y & 0x0f, $z); - } - - public function setBlock($x, $y, $z, $blockId = null, $meta = null){ - try{ - $this->hasChanged = true; - return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f); - }catch(ChunkException $e){ - $level = $this->getProvider(); - $this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y)); - return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f); - } - } - - public function getBlockId($x, $y, $z){ - return $this->sections[$y >> 4]->getBlockId($x, $y & 0x0f, $z); - } - - public function setBlockId($x, $y, $z, $id){ - try{ - $this->sections[$y >> 4]->setBlockId($x, $y & 0x0f, $z, $id); - $this->hasChanged = true; - }catch(ChunkException $e){ - $level = $this->getProvider(); - $this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y)); - $this->setBlockId($x, $y, $z, $id); - } - } - - public function getBlockData($x, $y, $z){ - return $this->sections[$y >> 4]->getBlockData($x, $y & 0x0f, $z); - } - - public function setBlockData($x, $y, $z, $data){ - try{ - $this->sections[$y >> 4]->setBlockData($x, $y & 0x0f, $z, $data); - $this->hasChanged = true; - }catch(ChunkException $e){ - $level = $this->getProvider(); - $this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y)); - $this->setBlockData($x, $y, $z, $data); - } - } - - public function getBlockSkyLight($x, $y, $z){ - return $this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z); - } - - public function setBlockSkyLight($x, $y, $z, $data){ - try{ - $this->sections[$y >> 4]->setBlockSkyLight($x, $y & 0x0f, $z, $data); - $this->hasChanged = true; - }catch(ChunkException $e){ - $level = $this->getProvider(); - $this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y)); - $this->setBlockSkyLight($x, $y, $z, $data); - } - } - - public function getBlockLight($x, $y, $z){ - return $this->sections[$y >> 4]->getBlockLight($x, $y & 0x0f, $z); - } - - public function setBlockLight($x, $y, $z, $data){ - try{ - $this->sections[$y >> 4]->setBlockLight($x, $y & 0x0f, $z, $data); - $this->hasChanged = true; - }catch(ChunkException $e){ - $level = $this->getProvider(); - $this->setInternalSection($Y = $y >> 4, $level::createChunkSection($Y)); - $this->setBlockLight($x, $y, $z, $data); - } - } - - public function getBlockIdColumn($x, $z){ - $column = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $column .= $this->sections[$y]->getBlockIdColumn($x, $z); - } - - return $column; - } - - public function getBlockDataColumn($x, $z){ - $column = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $column .= $this->sections[$y]->getBlockDataColumn($x, $z); - } - - return $column; - } - - public function getBlockSkyLightColumn($x, $z){ - $column = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $column .= $this->sections[$y]->getBlockSkyLightColumn($x, $z); - } - - return $column; - } - - public function getBlockLightColumn($x, $z){ - $column = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $column .= $this->sections[$y]->getBlockLightColumn($x, $z); - } - - return $column; - } - - public function isSectionEmpty($fY){ - return $this->sections[(int) $fY] instanceof EmptyChunkSection; - } - - public function getSection($fY){ - return $this->sections[(int) $fY]; - } - - public function setSection($fY, ChunkSection $section){ - if(substr_count($section->getIdArray(), "\x00") === 4096 and substr_count($section->getDataArray(), "\x00") === 2048){ - $this->sections[(int) $fY] = new EmptyChunkSection($fY); - }else{ - $this->sections[(int) $fY] = $section; - } - $this->hasChanged = true; - } - - private function setInternalSection($fY, ChunkSection $section){ - $this->sections[(int) $fY] = $section; - $this->hasChanged = true; - } - - public function load($generate = true){ - return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof Chunk; - } - - public function getBlockIdArray(){ - $blocks = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $blocks .= $this->sections[$y]->getIdArray(); - } - - return $blocks; - } - - public function getBlockDataArray(){ - $data = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $data .= $this->sections[$y]->getDataArray(); - } - - return $data; - } - - public function getBlockSkyLightArray(){ - $skyLight = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $skyLight .= $this->sections[$y]->getSkyLightArray(); - } - - return $skyLight; - } - - public function getBlockLightArray(){ - $blockLight = ""; - for($y = 0; $y < Chunk::SECTION_COUNT; ++$y){ - $blockLight .= $this->sections[$y]->getLightArray(); - } - - return $blockLight; - } - - /** - * @return ChunkSection[] - */ - public function getSections(){ - return $this->sections; - } - -} \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/BaseFullChunk.php b/src/pocketmine/level/format/generic/BaseFullChunk.php deleted file mode 100644 index 0cc8aa6a0..000000000 --- a/src/pocketmine/level/format/generic/BaseFullChunk.php +++ /dev/null @@ -1,455 +0,0 @@ -provider = $provider; - $this->x = (int) $x; - $this->z = (int) $z; - - $this->blocks = $blocks; - $this->data = $data; - $this->skyLight = $skyLight; - $this->blockLight = $blockLight; - - if(count($biomeColors) === 256){ - $this->biomeColors = $biomeColors; - }else{ - $this->biomeColors = array_fill(0, 256, 0); - } - - if(count($heightMap) === 256){ - $this->heightMap = $heightMap; - }else{ - $this->heightMap = array_fill(0, 256, 127); - } - - $this->extraData = $extraData; - - $this->NBTtiles = $tiles; - $this->NBTentities = $entities; - } - - protected function checkOldBiomes($data){ - if(strlen($data) !== 256){ - return; - } - - for($x = 0; $x < 16; ++$x){ - for($z = 0; $z < 16; ++$z){ - $biome = Biome::getBiome(ord($data{($z << 4) + $x})); - $this->setBiomeId($x, $z, $biome->getId()); - $c = $biome->getColor(); - $this->setBiomeColor($x, $z, $c >> 16, ($c >> 8) & 0xff, $c & 0xff); - } - } - } - - public function initChunk(){ - if($this->getProvider() instanceof LevelProvider and !$this->isInit){ - $changed = false; - if($this->NBTentities !== null){ - $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); - foreach($this->NBTentities as $nbt){ - if($nbt instanceof CompoundTag){ - if(!isset($nbt->id)){ - $this->setChanged(); - continue; - } - - if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){ - $changed = true; - continue; //Fixes entities allocated in wrong chunks. - } - - if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){ - $entity->spawnToAll(); - }else{ - $changed = true; - continue; - } - } - } - $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); - - $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); - foreach($this->NBTtiles as $nbt){ - if($nbt instanceof CompoundTag){ - if(!isset($nbt->id)){ - $changed = true; - continue; - } - - if(($nbt["x"] >> 4) !== $this->x or ($nbt["z"] >> 4) !== $this->z){ - $changed = true; - continue; //Fixes tiles allocated in wrong chunks. - } - - if(Tile::createTile($nbt["id"], $this, $nbt) === null){ - $changed = true; - continue; - } - } - } - - $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); - - $this->NBTentities = null; - $this->NBTtiles = null; - } - - $this->setChanged($changed); - - $this->isInit = true; - } - } - - public function getX(){ - return $this->x; - } - - public function getZ(){ - return $this->z; - } - - public function setX($x){ - $this->x = $x; - } - - public function setZ($z){ - $this->z = $z; - } - - /** - * @return LevelProvider - */ - public function getProvider(){ - return $this->provider; - } - - public function setProvider(LevelProvider $provider){ - $this->provider = $provider; - } - - public function getBiomeId($x, $z){ - return ($this->biomeColors[($z << 4) + $x] & 0xFF000000) >> 24; - } - - public function setBiomeId($x, $z, $biomeId){ - $this->hasChanged = true; - $this->biomeColors[($z << 4) + $x] = ($this->biomeColors[($z << 4) + $x] & 0xFFFFFF) | ($biomeId << 24); - } - - public function getBiomeColor($x, $z){ - $color = $this->biomeColors[($z << 4) + $x] & 0xFFFFFF; - - return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF]; - } - - public function setBiomeColor($x, $z, $R, $G, $B){ - $this->hasChanged = true; - $this->biomeColors[($z << 4) + $x] = ($this->biomeColors[($z << 4) + $x] & 0xFF000000) | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF); - } - - public function getHeightMap($x, $z){ - return $this->heightMap[($z << 4) + $x]; - } - - public function setHeightMap($x, $z, $value){ - $this->heightMap[($z << 4) + $x] = $value; - } - - public function recalculateHeightMap(){ - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false)); - } - } - } - - public function getBlockExtraData($x, $y, $z){ - if(isset($this->extraData[$index = Level::chunkBlockHash($x, $y, $z)])){ - return $this->extraData[$index]; - } - - return 0; - } - - public function setBlockExtraData($x, $y, $z, $data){ - if($data === 0){ - unset($this->extraData[Level::chunkBlockHash($x, $y, $z)]); - }else{ - $this->extraData[Level::chunkBlockHash($x, $y, $z)] = $data; - } - - $this->setChanged(true); - } - - public function populateSkyLight(){ - for($z = 0; $z < 16; ++$z){ - for($x = 0; $x < 16; ++$x){ - $top = $this->getHeightMap($x, $z); - for($y = 127; $y > $top; --$y){ - $this->setBlockSkyLight($x, $y, $z, 15); - } - - for($y = $top; $y >= 0; --$y){ - if(Block::$solid[$this->getBlockId($x, $y, $z)]){ - break; - } - - $this->setBlockSkyLight($x, $y, $z, 15); - } - - $this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false)); - } - } - } - - public function getHighestBlockAt($x, $z, $cache = true){ - if($cache){ - $h = $this->getHeightMap($x, $z); - - if($h !== 0 and $h !== 127){ - return $h; - } - } - - $column = $this->getBlockIdColumn($x, $z); - for($y = 127; $y >= 0; --$y){ - if($column{$y} !== "\x00"){ - $this->setHeightMap($x, $z, $y); - return $y; - } - } - - return 0; - } - - public function addEntity(Entity $entity){ - $this->entities[$entity->getId()] = $entity; - if(!($entity instanceof Player) and $this->isInit){ - $this->hasChanged = true; - } - } - - public function removeEntity(Entity $entity){ - unset($this->entities[$entity->getId()]); - if(!($entity instanceof Player) and $this->isInit){ - $this->hasChanged = true; - } - } - - public function addTile(Tile $tile){ - $this->tiles[$tile->getId()] = $tile; - if(isset($this->tileList[$index = (($tile->z & 0x0f) << 12) | (($tile->x & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){ - $this->tileList[$index]->close(); - } - $this->tileList[$index] = $tile; - if($this->isInit){ - $this->hasChanged = true; - } - } - - public function removeTile(Tile $tile){ - unset($this->tiles[$tile->getId()]); - unset($this->tileList[(($tile->z & 0x0f) << 12) | (($tile->x & 0x0f) << 8) | ($tile->y & 0xff)]); - if($this->isInit){ - $this->hasChanged = true; - } - } - - public function getEntities(){ - return $this->entities; - } - - public function getTiles(){ - return $this->tiles; - } - - public function getBlockExtraDataArray(){ - return $this->extraData; - } - - public function getTile($x, $y, $z){ - $index = ($z << 12) | ($x << 8) | $y; - return isset($this->tileList[$index]) ? $this->tileList[$index] : null; - } - - public function isLoaded(){ - return $this->getProvider() === null ? false : $this->getProvider()->isChunkLoaded($this->getX(), $this->getZ()); - } - - public function load($generate = true){ - return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof FullChunk; - } - - public function unload($save = true, $safe = true){ - $level = $this->getProvider(); - if($level === null){ - return true; - } - if($save === true and $this->hasChanged){ - $level->saveChunk($this->getX(), $this->getZ()); - } - if($safe === true){ - foreach($this->getEntities() as $entity){ - if($entity instanceof Player){ - return false; - } - } - } - - foreach($this->getEntities() as $entity){ - if($entity instanceof Player){ - continue; - } - $entity->close(); - } - foreach($this->getTiles() as $tile){ - $tile->close(); - } - $this->provider = null; - return true; - } - - public function getBlockIdArray(){ - return $this->blocks; - } - - public function getBlockDataArray(){ - return $this->data; - } - - public function getBlockSkyLightArray(){ - return $this->skyLight; - } - - public function getBlockLightArray(){ - return $this->blockLight; - } - - public function getBiomeIdArray(){ - $ids = ""; - foreach($this->biomeColors as $d){ - $ids .= chr(($d & 0xFF000000) >> 24); - } - return $ids; - } - - public function getBiomeColorArray(){ - return $this->biomeColors; - } - - public function getHeightMapArray(){ - return $this->heightMap; - } - - public function hasChanged(){ - return $this->hasChanged; - } - - public function setChanged($changed = true){ - $this->hasChanged = (bool) $changed; - } - - public static function fromFastBinary($data, LevelProvider $provider = null){ - return static::fromBinary($data, $provider); - } - - public function toFastBinary(){ - return $this->toBinary(); - } - - public function isLightPopulated(){ - return true; - } - - public function setLightPopulated($value = 1){ - - } - -} diff --git a/src/pocketmine/level/format/generic/BaseLevelProvider.php b/src/pocketmine/level/format/generic/BaseLevelProvider.php index ebde0ef85..c428787a2 100644 --- a/src/pocketmine/level/format/generic/BaseLevelProvider.php +++ b/src/pocketmine/level/format/generic/BaseLevelProvider.php @@ -126,5 +126,44 @@ abstract class BaseLevelProvider implements LevelProvider{ file_put_contents($this->getPath() . "level.dat", $buffer); } + public function requestChunkTask($x, $z){ + $chunk = $this->getChunk($x, $z, false); + if(!($chunk instanceof GenericChunk)){ + throw new ChunkException("Invalid Chunk sent"); + } + /* + $tiles = ""; + + if(count($chunk->getTiles()) > 0){ + $nbt = new NBT(NBT::LITTLE_ENDIAN); + $list = []; + foreach($chunk->getTiles() as $tile){ + if($tile instanceof Spawnable){ + $list[] = $tile->getSpawnCompound(); + } + } + $nbt->setData($list); + $tiles = $nbt->write(true); + } + + $extraData = new BinaryStream(); + $extraData->putLInt(count($chunk->getBlockExtraDataArray())); + foreach($chunk->getBlockExtraDataArray() as $key => $value){ + $extraData->putLInt($key); + $extraData->putLShort($value); + } + + $ordered = $chunk->getBlockIdArray() . + $chunk->getBlockDataArray() . + $chunk->getBlockSkyLightArray() . + $chunk->getBlockLightArray() . + pack("C*", ...$chunk->getHeightMapArray()) . + pack("N*", ...$chunk->getBiomeColorArray()) . + $extraData->getBuffer() . + $tiles; + */ + + $this->getLevel()->chunkRequestCallback($x, $z, $chunk->networkSerialize()); + } } diff --git a/src/pocketmine/level/format/anvil/ChunkRequestTask.php b/src/pocketmine/level/format/generic/ChunkRequestTask.php similarity index 52% rename from src/pocketmine/level/format/anvil/ChunkRequestTask.php rename to src/pocketmine/level/format/generic/ChunkRequestTask.php index f86f36fbb..f0a41c3a3 100644 --- a/src/pocketmine/level/format/anvil/ChunkRequestTask.php +++ b/src/pocketmine/level/format/generic/ChunkRequestTask.php @@ -19,7 +19,7 @@ * */ -namespace pocketmine\level\format\anvil; +namespace pocketmine\level\format; use pocketmine\level\Level; use pocketmine\nbt\NBT; @@ -40,10 +40,11 @@ class ChunkRequestTask extends AsyncTask{ public function __construct(Level $level, Chunk $chunk){ $this->levelId = $level->getId(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastSerialize($chunk); $this->chunkX = $chunk->getX(); $this->chunkZ = $chunk->getZ(); + //TODO: serialize tiles with chunks $tiles = ""; $nbt = new NBT(NBT::LITTLE_ENDIAN); foreach($chunk->getTiles() as $tile){ @@ -57,63 +58,13 @@ class ChunkRequestTask extends AsyncTask{ } public function onRun(){ + $chunk = GenericChunk::fastDeserialize($this->chunk); - $chunk = Chunk::fromFastBinary($this->chunk); - $ids = $chunk->getBlockIdArray(); - $meta = $chunk->getBlockDataArray(); - $blockLight = $chunk->getBlockLightArray(); - $skyLight = $chunk->getBlockSkyLightArray(); - - - $orderedIds = ""; - $orderedData = ""; - $orderedSkyLight = ""; - $orderedLight = ""; - - - for($x = 0; $x < 16; ++$x){ - for($z = 0; $z < 16; ++$z){ - $orderedIds .= $this->getColumn($ids, $x, $z); - $orderedData .= $this->getHalfColumn($meta, $x, $z); - $orderedSkyLight .= $this->getHalfColumn($skyLight, $x, $z); - $orderedLight .= $this->getHalfColumn($blockLight, $x, $z); - } - } - - $heightmap = pack("C*", ...$chunk->getHeightMapArray()); - $biomeColors = pack("N*", ...$chunk->getBiomeColorArray()); - - $ordered = $orderedIds . $orderedData . $orderedSkyLight . $orderedLight . $heightmap . $biomeColors . $this->tiles; + $ordered = $chunk->networkSerialize(); $this->setResult($ordered, false); } - public function getColumn($data, $x, $z){ - $column = ""; - $i = ($z << 4) + $x; - for($y = 0; $y < 128; ++$y){ - $column .= $data{($y << 8) + $i}; - } - - return $column; - } - - public function getHalfColumn($data, $x, $z){ - $column = ""; - $i = ($z << 3) + ($x >> 1); - if(($x & 1) === 0){ - for($y = 0; $y < 128; $y += 2){ - $column .= ($data{($y << 7) + $i} & "\x0f") | chr((ord($data{(($y + 1) << 7) + $i}) & 0x0f) << 4); - } - }else{ - for($y = 0; $y < 128; $y += 2){ - $column .= chr((ord($data{($y << 7) + $i}) & 0xf0) >> 4) | ($data{(($y + 1) << 7) + $i} & "\xf0"); - } - } - - return $column; - } - public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level instanceof Level and $this->hasResult()){ diff --git a/src/pocketmine/level/format/generic/EmptyChunkSection.php b/src/pocketmine/level/format/generic/EmptyChunkSection.php deleted file mode 100644 index b92b19085..000000000 --- a/src/pocketmine/level/format/generic/EmptyChunkSection.php +++ /dev/null @@ -1,118 +0,0 @@ -y = $y; - } - - final public function getY(){ - return $this->y; - } - - final public function getBlockId($x, $y, $z){ - return 0; - } - - final public function getBlockIdColumn($x, $z){ - return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - } - - final public function getBlockDataColumn($x, $z){ - return "\x00\x00\x00\x00\x00\x00\x00\x00"; - } - - final public function getBlockSkyLightColumn($x, $z){ - return "\xff\xff\xff\xff\xff\xff\xff\xff"; - } - - final public function getBlockLightColumn($x, $z){ - return "\x00\x00\x00\x00\x00\x00\x00\x00"; - } - - final public function getFullBlock($x, $y, $z){ - return 0; - } - - final public function getBlock($x, $y, $z, &$id = null, &$meta = null){ - $id = 0; - $meta = 0; - } - - final public function setBlock($x, $y, $z, $id = null, $meta = null){ - throw new ChunkException("Tried to modify an empty Chunk"); - } - - public function getIdArray(){ - return str_repeat("\x00", 4096); - } - - public function getDataArray(){ - return str_repeat("\x00", 2048); - } - - public function getSkyLightArray(){ - return str_repeat("\xff", 2048); - } - - public function getLightArray(){ - return str_repeat("\x00", 2048); - } - - final public function setBlockId($x, $y, $z, $id){ - throw new ChunkException("Tried to modify an empty Chunk"); - } - - final public function getBlockData($x, $y, $z){ - return 0; - } - - final public function setBlockData($x, $y, $z, $data){ - throw new ChunkException("Tried to modify an empty Chunk"); - } - - final public function getBlockLight($x, $y, $z){ - return 0; - } - - final public function setBlockLight($x, $y, $z, $level){ - throw new ChunkException("Tried to modify an empty Chunk"); - } - - final public function getBlockSkyLight($x, $y, $z){ - return 15; - } - - final public function setBlockSkyLight($x, $y, $z, $level){ - throw new ChunkException("Tried to modify an empty Chunk"); - } -} \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/EmptySubChunk.php b/src/pocketmine/level/format/generic/EmptySubChunk.php new file mode 100644 index 000000000..16e2a53d4 --- /dev/null +++ b/src/pocketmine/level/format/generic/EmptySubChunk.php @@ -0,0 +1,108 @@ +y = $y; + } + + public function isEmpty() : bool{ + return true; + } + + public function getBlockId(int $x, int $y, int $z) : int{ + return 0; + } + + public function setBlockId(int $x, int $y, int $z, int $id){ + throw new ChunkException("Tried to modify nonexistent subchunk"); + } + + public function getBlockData(int $x, int $y, int $z) : int{ + return 0; + } + + public function setBlockData(int $x, int $y, int $z, int $data){ + throw new ChunkException("Tried to modify nonexistent subchunk"); + } + + public function getFullBlock(int $x, int $y, int $z) : int{ + return 0; + } + + public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{ + throw new ChunkException("Tried to modify nonexistent subchunk"); + } + + public function getBlockSkyLight(int $x, int $y, int $z) : int{ + return 15; + } + + public function setBlockSkyLight(int $x, int $y, int $z, int $level){ + throw new ChunkException("Tried to modify nonexistent subchunk"); + } + + public function getBlockLight(int $x, int $y, int $z) : int{ + return 0; + } + + public function setBlockLight(int $x, int $y, int $z, int $level){ + throw new ChunkException("Tried to modify nonexistent subchunk"); + } + + public function getBlockIdColumn(int $x, int $z) : string{ + return "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + public function getBlockDataColumn(int $x, int $z) : string{ + return "\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + public function getBlockLightColumn(int $x, int $z) : string{ + return "\x00\x00\x00\x00\x00\x00\x00\x00"; + } + + public function getSkyLightColumn(int $x, int $z) : string{ + return "\xff\xff\xff\xff\xff\xff\xff\xff"; + } + + public function getBlockIdArray() : string{ + return str_repeat("\x00", 4096); + } + + public function getBlockDataArray() : string{ + return str_repeat("\x00", 2048); + } + + public function getBlockLightArray() : string{ + return str_repeat("\x00", 2048); + } + + public function getSkyLightArray() : string{ + return str_repeat("\xff", 2048); + } +} \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/GenericChunk.php b/src/pocketmine/level/format/generic/GenericChunk.php new file mode 100644 index 000000000..d5d4e0197 --- /dev/null +++ b/src/pocketmine/level/format/generic/GenericChunk.php @@ -0,0 +1,757 @@ +provider = $provider; + $this->x = $chunkX; + $this->z = $chunkZ; + + $this->height = $provider !== null ? ($provider->getWorldHeight() >> 4) : 16; + + foreach($subChunks as $subChunk){ + $y = $subChunk->getY(); + if($y < 0 or $y >= $this->height){ + throw new ChunkException("Invalid subchunk index $y!"); + } + if($subChunk->isEmpty()){ + $this->subChunks[$y] = new EmptySubChunk($y); + }else{ + $this->subChunks[$y] = $subChunk; + } + } + + for($i = 0; $i < $this->height; ++$i){ + if(!isset($this->subChunks[$i])){ + $this->subChunks[$i] = new EmptySubChunk($i); + } + } + + if(count($heightMap) === 256){ + $this->heightMap = $heightMap; + }else{ + assert(count($heightMap) === 0, "Wrong HeightMap value count, expected 256, got " . count($heightMap)); + $this->heightMap = array_fill(0, 256, 0); + } + + if(count($biomeColors) === 256){ + $this->biomeColors = $biomeColors; + }else{ + assert(count($biomeColors) === 0, "Wrong HeightMap value count, expected 256, got " . count($biomeColors)); + $this->biomeColors = array_fill(0, 256, 0); + } + + $this->NBTtiles = $tiles; + $this->NBTentities = $entities; + } + + public function getX() : int{ + return $this->x; + } + + public function getZ() : int{ + return $this->z; + } + + public function setX(int $x){ + $this->x = $x; + } + + public function setZ(int $z){ + $this->z = $z; + } + + public function getProvider(){ + return $this->provider; + } + + public function setProvider(LevelProvider $provider){ + $this->provider = $provider; + } + + public function getHeight() : int{ + return $this->height; + } + + public function getFullBlock(int $x, int $y, int $z) : int{ + return $this->getSubChunk($y >> 4)->getFullBlock($x, $y & 0x0f, $z); + } + + public function setBlock(int $x, int $y, int $z, $blockId = null, $meta = null){ + $this->getSubChunk($y >> 4, true)->setBlock($x, $y & 0x0f, $z, $blockId !== null ? ($blockId & 0xff) : null, $meta !== null ? ($meta & 0x0f) : null); + } + + public function getBlockId(int $x, int $y, int $z) : int{ + return $this->getSubChunk($y >> 4)->getBlockId($x, $y & 0x0f, $z); + } + + public function setBlockId(int $x, int $y, int $z, int $id){ + $this->getSubChunk($y >> 4, true)->setBlockId($x, $y & 0x0f, $z, $id); + } + + public function getBlockData(int $x, int $y, int $z) : int{ + return $this->getSubChunk($y >> 4)->getBlockData($x, $y & 0x0f, $z); + } + + public function setBlockData(int $x, int $y, int $z, int $data){ + $this->getSubChunk($y >> 4, true)->setBlockData($x, $y & 0x0f, $z, $data); + } + + public function getBlockExtraData(int $x, int $y, int $z) : int{ + return $this->extraData[Level::chunkBlockHash($x, $y, $z)] ?? 0; + } + + public function setBlockExtraData(int $x, int $y, int $z, int $data){ + if($data === 0){ + unset($this->extraData[Level::chunkBlockHash($x, $y, $z)]); + }else{ + $this->extraData[Level::chunkBlockHash($x, $y, $z)] = $data; + } + + $this->hasChanged = true; + } + + public function getBlockSkyLight(int $x, int $y, int $z) : int{ + return $this->getSubChunk($y >> 4)->getBlockSkyLight($x, $y & 0x0f, $z); + } + + public function setBlockSkyLight(int $x, int $y, int $z, int $level){ + $this->getSubChunk($y >> 4, true)->setBlockSkyLight($x, $y & 0x0f, $z, $level); + } + + public function getBlockLight(int $x, int $y, int $z) : int{ + return $this->getSubChunk($y >> 4)->getBlockLight($x, $y & 0x0f, $z); + } + + public function setBlockLight(int $x, int $y, int $z, int $level){ + $this->getSubChunk($y >> 4, true)->setBlockLight($x, $y & 0x0f, $z, $level); + } + + public function getHighestBlockAt(int $x, int $z, bool $useHeightMap = true) : int{ + if($useHeightMap){ + $height = $this->getHeightMap($x, $z); + + if($height !== 0 and $height !== 255){ + return $height; + } + } + + $index = $this->getHighestSubChunkIndex(); + if($index < 0){ + return 0; + } + + $height = $index << 4; + + for($y = $index; $y >= 0; --$y){ + $height = $this->getSubChunk($y)->getHighestBlockAt($x, $z) | ($y << 4); + if($height !== -1){ + break; + } + } + + $this->setHeightMap($x, $z, $height); + return $height; + } + + public function getHeightMap(int $x, int $z) : int{ + return $this->heightMap[($z << 4) | $x]; + } + + public function setHeightMap(int $x, int $z, int $value){ + $this->heightMap[($z << 4) | $x] = $value; + } + + public function recalculateHeightMap(){ + for($z = 0; $z < 16; ++$z){ + for($x = 0; $x < 16; ++$x){ + $this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false)); + } + } + } + + public function populateSkyLight(){ + for($x = 0; $x < 16; ++$x){ + for($z = 0; $z < 16; ++$z){ + $top = $this->getHeightMap($x, $z); + for($y = 127; $y > $top; --$y){ + $this->setBlockSkyLight($x, $y, $z, 15); + } + + for($y = $top; $y >= 0; --$y){ + if(Block::$solid[$this->getBlockId($x, $y, $z)]){ + break; + } + + $this->setBlockSkyLight($x, $y, $z, 15); + } + + $this->setHeightMap($x, $z, $this->getHighestBlockAt($x, $z, false)); + } + } + } + + public function getBiomeId(int $x, int $z) : int{ + return ($this->biomeColors[($z << 4) | $x] & 0xFF000000) >> 24; + } + + public function setBiomeId(int $x, int $z, int $biomeId){ + $this->hasChanged = true; + $this->biomeColors[($z << 4) | $x] = ($this->biomeColors[($z << 4) | $x] & 0xFFFFFF) | ($biomeId << 24); + } + + public function getBiomeColor(int $x, int $z) : int{ + $color = $this->biomeColors[($z << 4) | $x] & 0xFFFFFF; + + return [$color >> 16, ($color >> 8) & 0xFF, $color & 0xFF]; + } + + public function setBiomeColor(int $x, int $z, int $R, int $G, int $B){ + $this->hasChanged = true; + $this->biomeColors[($z << 4) | $x] = ($this->biomeColors[($z << 4) | $x] & 0xFF000000) | (($R & 0xFF) << 16) | (($G & 0xFF) << 8) | ($B & 0xFF); + } + + public function getBlockIdColumn(int $x, int $z) : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockIdColumn($x, $z); + } + return $result; + } + + public function getBlockDataColumn(int $x, int $z) : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockDataColumn($x, $z); + } + return $result; + } + + public function getBlockSkyLightColumn(int $x, int $z) : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getSkyLightColumn($x, $z); + } + return $result; + } + + public function getBlockLightColumn(int $x, int $z) : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockLightColumn($x, $z); + } + return $result; + } + + public function isLightPopulated() : bool{ + return $this->lightPopulated; + } + + public function setLightPopulated(bool $value = true){ + $this->lightPopulated = $value; + } + + public function isPopulated() : bool{ + return $this->terrainPopulated; + } + + public function setPopulated(bool $value = true){ + $this->terrainPopulated = $value; + } + + public function isGenerated() : bool{ + return $this->terrainGenerated; + } + + public function setGenerated(bool $value = true){ + $this->terrainGenerated = $value; + } + + public function addEntity(Entity $entity){ + $this->entities[$entity->getId()] = $entity; + if(!($entity instanceof Player) and $this->isInit){ + $this->hasChanged = true; + } + } + + public function removeEntity(Entity $entity){ + unset($this->entities[$entity->getId()]); + if(!($entity instanceof Player) and $this->isInit){ + $this->hasChanged = true; + } + } + + public function addTile(Tile $tile){ + $this->tiles[$tile->getId()] = $tile; + if(isset($this->tileList[$index = (($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]) and $this->tileList[$index] !== $tile){ + $this->tileList[$index]->close(); + } + $this->tileList[$index] = $tile; + if($this->isInit){ + $this->hasChanged = true; + } + } + + public function removeTile(Tile $tile){ + unset($this->tiles[$tile->getId()]); + unset($this->tileList[(($tile->x & 0x0f) << 12) | (($tile->z & 0x0f) << 8) | ($tile->y & 0xff)]); + if($this->isInit){ + $this->hasChanged = true; + } + } + + public function getEntities() : array{ + return $this->entities; + } + + public function getTiles() : array{ + return $this->tiles; + } + + public function getTile(int $x, int $y, int $z){ + $index = ($x << 12) | ($z << 8) | $y; + return $this->tileList[$index] ?? null; + } + + public function isLoaded() : bool{ + return $this->getProvider() === null ? false : $this->getProvider()->isChunkLoaded($this->getX(), $this->getZ()); + } + + public function load(bool $generate = true) : bool{ + return $this->getProvider() === null ? false : $this->getProvider()->getChunk($this->getX(), $this->getZ(), true) instanceof GenericChunk; + } + + public function unload(bool $save = true, bool $safe = true) : bool{ + $level = $this->getProvider(); + if($level === null){ + return true; + } + if($save === true and $this->hasChanged){ + $level->saveChunk($this->getX(), $this->getZ()); + } + if($safe === true){ + foreach($this->getEntities() as $entity){ + if($entity instanceof Player){ + return false; + } + } + } + + foreach($this->getEntities() as $entity){ + if($entity instanceof Player){ + continue; + } + $entity->close(); + } + foreach($this->getTiles() as $tile){ + $tile->close(); + } + $this->provider = null; + return true; + } + + public function initChunk(){ + if($this->getProvider() instanceof LevelProvider and !$this->isInit){ + $changed = false; + if($this->NBTentities !== null){ + $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); + foreach($this->NBTentities as $nbt){ + if($nbt instanceof CompoundTag){ + if(!isset($nbt->id)){ + $changed = true; + continue; + } + + if(($nbt["Pos"][0] >> 4) !== $this->x or ($nbt["Pos"][2] >> 4) !== $this->z){ + $changed = true; + continue; //Fixes entities allocated in wrong chunks. + } + + if(($entity = Entity::createEntity($nbt["id"], $this, $nbt)) instanceof Entity){ + $entity->spawnToAll(); + }else{ + $changed = true; + continue; + } + } + } + $this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); + + $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); + foreach($this->NBTtiles as $nbt){ + if($nbt instanceof CompoundTag){ + if(!isset($nbt->id)){ + $changed = true; + continue; + } + + if(($nbt["x"] >> 4) !== $this->x or ($nbt["z"] >> 4) !== $this->z){ + $changed = true; + continue; //Fixes tiles allocated in wrong chunks. + } + + if(Tile::createTile($nbt["id"], $this, $nbt) === null){ + $changed = true; + continue; + } + } + } + + $this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); + + $this->NBTentities = null; + $this->NBTtiles = null; + } + + $this->hasChanged = $changed; + + $this->isInit = true; + } + } + + public function getBiomeIdArray() : string{ + $ids = str_repeat("\x00", 256); + foreach($this->biomeColors as $i => $d){ + $ids{$i} = chr(($d & 0xFF000000) >> 24); + } + return $ids; + } + + public function getBiomeColorArray() : array{ + return $this->biomeColors; + } + + public function getHeightMapArray() : array{ + return $this->heightMap; + } + + public function getBlockIdArray() : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockIdArray(); + } + return $result; + } + + public function getBlockDataArray() : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockDataArray(); + } + return $result; + } + + public function getBlockExtraDataArray() : array{ + return $this->extraData; + } + + public function getBlockSkyLightArray() : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getSkyLightArray(); + } + return $result; + } + + public function getBlockLightArray() : string{ + $result = ""; + foreach($this->subChunks as $subChunk){ + $result .= $subChunk->getBlockLightArray(); + } + return $result; + } + + public function hasChanged() : bool{ + return $this->hasChanged; + } + + public function setChanged(bool $value = true){ + $this->hasChanged = $value; + } + + public function getSubChunk(int $fY, bool $generateNew = false) : SubChunk{ + if($fY < 0 or $fY >= self::MAX_SUBCHUNKS){ + return new EmptySubChunk($fY); + }elseif($generateNew and $this->subChunks[$fY] instanceof EmptySubChunk){ + $this->subChunks[$fY] = new SubChunk($fY); + } + return $this->subChunks[$fY]; + } + + public function setSubChunk(int $fY, SubChunk $subChunk = null, bool $allowEmpty = false) : bool{ + if($fY < 0 or $fY >= self::MAX_SUBCHUNKS){ + return false; + } + if($subChunk === null or ($subChunk->isEmpty() and !$allowEmpty)){ + $this->subChunks[$fY] = new EmptySubChunk(); + }else{ + $this->subChunks[$fY] = $subChunk; + } + $this->hasChanged = true; + return true; + } + + public function getSubChunks() : array{ + return $this->subChunks; + } + + public function getHighestSubChunkIndex() : int{ + for($y = count($this->subChunks) - 1; $y >= 0; --$y){ + if($this->subChunks[$y] === null or $this->subChunks[$y]->isEmpty()){ + continue; + } + break; + } + + return $y; + } + + public function getSubChunkSendCount() : int{ + return $this->getHighestSubChunkIndex() + 1; + } + + public function getNonEmptySubChunkCount() : int{ + $result = 0; + foreach($this->subChunks as $subChunk){ + if($subChunk->isEmpty()){ + continue; + } + ++$result; + } + return $result; + } + + public function clearEmptySubChunks(){ + foreach($this->subChunks as $y => $subChunk){ + if($subChunk->isEmpty()){ + if($y < 0 or $y > self::MAX_SUBCHUNKS){ + assert(false, "Invalid subchunk index"); + unset($this->subChunks[$y]); + }elseif($subChunk instanceof EmptySubChunk){ + continue; + }else{ + $this->subChunks[$y] = new EmptySubChunk($y); + } + $this->hasChanged = true; + } + } + } + + public function networkSerialize() : string{ + $result = ""; + $subChunkCount = $this->getSubChunkSendCount(); + $result .= chr($subChunkCount); + for($y = 0; $y < $subChunkCount; ++$y){ + $subChunk = $this->subChunks[$y]; + $result .= "\x00" //unknown byte! + . $subChunk->getBlockIdArray() + . $subChunk->getBlockDataArray() + . $subChunk->getSkyLightArray() + . $subChunk->getBlockLightArray(); + } + + //TODO: heightmaps, tile data + return $result; + } + + public static function fastSerialize(Chunk $chunk) : string{ + $stream = new BinaryStream(); + $stream->putInt($chunk->x); + $stream->putInt($chunk->z); + $count = 0; + $subChunks = ""; + foreach($chunk->subChunks as $subChunk){ + if($subChunk->isEmpty()){ + continue; + } + ++$count; + $subChunks .= chr($subChunk->getY()) + . $subChunk->getBlockIdArray() + . $subChunk->getBlockDataArray() + . $subChunk->getSkyLightArray() + . $subChunk->getBlockLightArray(); + } + $stream->putByte($count); + $stream->put($subChunks); + $stream->put(pack("C*", ...$chunk->getHeightMapArray()) . + pack("N*", ...$chunk->getBiomeColorArray()) . + chr(($chunk->lightPopulated ? 1 << 2 : 0) | ($chunk->terrainPopulated ? 1 << 1 : 0) | ($chunk->terrainGenerated ? 1 : 0))); + //TODO: tiles and entities + return $stream->getBuffer(); + } + + public static function fastDeserialize(string $data, LevelProvider $provider = null){ + $stream = new BinaryStream(); + $stream->setBuffer($data); + $data = null; + $x = $stream->getInt(); + $z = $stream->getInt(); + $subChunks = []; + $count = $stream->getByte(); + for($y = 0; $y < $count; ++$y){ + $subChunks[] = new SubChunk( + $stream->getByte(), //y + $stream->get(4096), //blockIds + $stream->get(2048), //blockData + $stream->get(2048), //skyLight + $stream->get(2048) //blockLight + ); + } + $heightMap = array_values(unpack("C*", $stream->get(256))); + $biomeColors = array_values(unpack("N*", $stream->get(1024))); + + $chunk = new GenericChunk($provider, $x, $z, $subChunks, $heightMap, $biomeColors); + $flags = $stream->getByte(); + $chunk->lightPopulated = (bool) ($flags & 4); + $chunk->terrainPopulated = (bool) ($flags & 2); + $chunk->terrainGenerated = (bool) ($flags & 1); + return $chunk; + } + + public static function getEmptyChunk(int $x, int $z, LevelProvider $provider = null) : Chunk{ + return new GenericChunk($provider, $x, $z); + } + + /** + * Re-orders a byte array (YZX -> XZY and vice versa) + * + * @param string $array length 4096 + * + * @return string length 4096 + */ + public static final function reorderByteArray(string $array) : string{ + $result = str_repeat("\x00", 4096); + for($x = 0; $x < 16; ++$x){ + for($z = 0; $z < 16; ++$z){ + for($y = 0; $y < 16; ++$y){ + $result{($x << 8) | ($z << 4) | $y} = $array{($y << 8) | ($z << 4) | $x}; + } + } + } + return $result; + } + + /** + * Re-orders a nibble array (YZX -> XZY and vice versa) + * + * @param string $array length 2048 + * + * @return string length 2048 + */ + public static final function reorderNibbleArray(string $array) : string{ + $result = str_repeat("\x00", 2048); + for($x = 0; $x < 16; ++$x){ + for($z = 0; $z < 16; ++$z){ + for($y = 0; $y < 16; ++$y){ + $inputIndex = (($y << 7) | ($z << 3) | ($x >> 1)); + $outputIndex = (($x << 7) | ($z << 3) | ($y >> 1)); + $current = ord($result{$outputIndex}); + $byte = ord($array{$inputIndex}); + + if(($y & 1) === 0){ + if(($x & 1) === 0){ + $current |= ($byte & 0x0f); + }else{ + $current |= (($byte >> 4) & 0x0f); + } + }else{ + if(($x & 1) === 0){ + $current |= (($byte << 4) & 0xf0); + }else{ + $current |= ($byte & 0xf0); + } + } + $result{$outputIndex} = chr($current); + } + } + } + return $result; + } + +} \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/SubChunk.php b/src/pocketmine/level/format/generic/SubChunk.php new file mode 100644 index 000000000..6fce6864d --- /dev/null +++ b/src/pocketmine/level/format/generic/SubChunk.php @@ -0,0 +1,205 @@ +y = $y; + self::assignData($this->ids, $ids, 4096); + self::assignData($this->data, $data, 2048); + self::assignData($this->blockLight, $blockLight, 2048); + self::assignData($this->skyLight, $skyLight, 2048, "\xff"); + } + + public function getY() : int{ + return $this->y; + } + + public function isEmpty() : bool{ + assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids)); + return substr_count($this->ids, "\x00") === 4096; + } + + public function getBlockId(int $x, int $y, int $z) : int{ + return ord($this->ids{($x << 8) | ($z << 4) | $y}); + } + + public function setBlockId(int $x, int $y, int $z, int $id){ + $this->ids{($x << 8) | ($z << 4) | $y} = chr($id); + } + + public function getBlockData(int $x, int $y, int $z) : int{ + $m = ord($this->data{($x << 7) + ($z << 3) + ($y >> 1)}); + if(($y & 1) === 0){ + return $m & 0x0f; + }else{ + return $m >> 4; + } + } + + public function setBlockData(int $x, int $y, int $z, int $data){ + $i = ($x << 7) | ($z << 3) | ($y >> 1); + $current = ord($this->data{$i}); + if(($y & 1) === 0){ + $this->data{$i} = chr(($current & 0xf0) | ($data & 0x0f)); + }else{ + $this->data{$i} = chr((($data & 0x0f) << 4) | ($current & 0x0f)); + } + $this->hasChanged = true; + } + + public function getFullBlock(int $x, int $y, int $z) : int{ + $i = ($x << 8) | ($z << 4) | $y; + if(($y & 1) === 0){ + return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0f); + }else{ + return (ord($this->ids{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4); + } + } + + public function setBlock(int $x, int $y, int $z, $id = null, $data = null) : bool{ + $i = ($x << 8) | ($z << 4) | $y; + $changed = false; + if($id !== null){ + $block = chr($id); + if($this->ids{$i} !== $block){ + $this->ids{$i} = $block; + $changed = true; + } + } + + if($data !== null){ + $i >>= 1; + $byte = ord($this->data{$i}); + if(($y & 1) === 0){ + $this->data{$i} = chr(($byte & 0xf0) | ($data & 0x0f)); + }else{ + $this->data{$i} = chr((($data & 0x0f) << 4) | ($byte & 0x0f)); + } + if($this->data{$i} !== $byte){ + $changed = true; + } + } + + return $changed; + } + + public function getBlockSkyLight(int $x, int $y, int $z) : int{ + $byte = ord($this->skyLight{($x << 7) + ($z << 3) + ($y >> 1)}); + if(($y & 1) === 0){ + return $byte & 0x0f; + }else{ + return $byte >> 4; + } + } + + public function setBlockSkyLight(int $x, int $y, int $z, int $level){ + $i = ($x << 7) + ($z << 3) + ($y >> 1); + $byte = ord($this->skyLight{$i}); + if(($y & 1) === 0){ + $this->skyLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f)); + }else{ + $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); + } + } + + public function getBlockLight(int $x, int $y, int $z) : int{ + $byte = ord($this->blockLight{($x << 7) + ($z << 3) + ($y >> 1)}); + if(($y & 1) === 0){ + return $byte & 0x0f; + }else{ + return $byte >> 4; + } + } + + public function setBlockLight(int $x, int $y, int $z, int $level){ + $i = ($x << 7) + ($z << 3) + ($y >> 1); + $byte = ord($this->blockLight{$i}); + if(($y & 1) === 0){ + $this->blockLight{$i} = chr(($byte & 0xf0) | ($level & 0x0f)); + }else{ + $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($byte & 0x0f)); + } + } + + public function getHighestBlockAt(int $x, int $z) : int{ + for($y = 15; $y >= 0; --$y){ + if($this->ids{($x << 8) | ($z << 4) | $y} !== "\x00"){ + return $y; + } + } + + return -1; //highest block not in this subchunk + } + + public function getBlockIdColumn(int $x, int $z) : string{ + return substr($this->ids, (($x << 8) | ($z << 4)), 16); + } + + public function getBlockDataColumn(int $x, int $z) : string{ + return substr($this->data, (($x << 7) | ($z << 3)), 8); + } + + public function getBlockLightColumn(int $x, int $z) : string{ + return substr($this->blockLight, (($x << 7) | ($z << 3)), 8); + } + + public function getSkyLightColumn(int $x, int $z) : string{ + return substr($this->skyLight, (($x << 7) | ($z << 3)), 8); + } + + public function getBlockIdArray() : string{ + assert(strlen($this->ids) === 4096, "Wrong length of ID array, expecting 4096 bytes, got " . strlen($this->ids)); + return $this->ids; + } + + public function getBlockDataArray() : string{ + assert(strlen($this->data) === 2048, "Wrong length of data array, expecting 2048 bytes, got " . strlen($this->data)); + return $this->data; + } + + public function getBlockLightArray() : string{ + assert(strlen($this->blockLight) === 2048, "Wrong length of light array, expecting 2048 bytes, got " . strlen($this->blockLight)); + return $this->data; + } + + public function getSkyLightArray() : string{ + assert(strlen($this->skyLight) === 2048, "Wrong length of skylight array, expecting 2048 bytes, got " . strlen($this->skyLight)); + return $this->data; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/format/leveldb/Chunk.php b/src/pocketmine/level/format/leveldb/Chunk.php index 0e4635091..3f5bb7d68 100644 --- a/src/pocketmine/level/format/leveldb/Chunk.php +++ b/src/pocketmine/level/format/leveldb/Chunk.php @@ -21,14 +21,14 @@ namespace pocketmine\level\format\leveldb; -use pocketmine\level\format\generic\BaseFullChunk; +use pocketmine\level\format\generic\BaseChunk; use pocketmine\level\format\LevelProvider; use pocketmine\nbt\NBT; use pocketmine\Player; use pocketmine\utils\Binary; use pocketmine\utils\BinaryStream; -class Chunk extends BaseFullChunk{ +class Chunk extends BaseChunk{ const DATA_LENGTH = 16384 * (2 + 1 + 1 + 1) + 256 + 1024; diff --git a/src/pocketmine/level/format/leveldb/LevelDB.php b/src/pocketmine/level/format/leveldb/LevelDB.php index 2d94241e9..ffa58ee45 100644 --- a/src/pocketmine/level/format/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/leveldb/LevelDB.php @@ -21,7 +21,7 @@ namespace pocketmine\level\format\leveldb; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\format\generic\BaseLevelProvider; use pocketmine\level\generator\Generator; use pocketmine\level\Level; @@ -259,7 +259,7 @@ class LevelDB extends BaseLevelProvider{ public function unloadChunk($x, $z, $safe = true){ $chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null; - if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){ + if($chunk instanceof Chunk and $chunk->unload(false, $safe)){ unset($this->chunks[$index]); return true; } @@ -302,7 +302,7 @@ class LevelDB extends BaseLevelProvider{ return $this->db; } - public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ + public function setChunk($chunkX, $chunkZ, Chunk $chunk){ if(!($chunk instanceof Chunk)){ throw new ChunkException("Invalid Chunk class"); } @@ -341,7 +341,7 @@ class LevelDB extends BaseLevelProvider{ public function isChunkPopulated($chunkX, $chunkZ){ $chunk = $this->getChunk($chunkX, $chunkZ); - if($chunk instanceof FullChunk){ + if($chunk instanceof Chunk){ return $chunk->isPopulated(); }else{ return false; diff --git a/src/pocketmine/level/format/mcregion/Chunk.php b/src/pocketmine/level/format/mcregion/Chunk.php deleted file mode 100644 index d37225a26..000000000 --- a/src/pocketmine/level/format/mcregion/Chunk.php +++ /dev/null @@ -1,462 +0,0 @@ -provider = $level; - $this->nbt = new CompoundTag("Level", []); - return; - } - - $this->nbt = $nbt; - - if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof ListTag){ - $this->nbt->Entities->setTagType(NBT::TAG_Compound); - }else{ - $this->nbt->Entities = new ListTag("Entities", []); - $this->nbt->Entities->setTagType(NBT::TAG_Compound); - } - - if(isset($this->nbt->TileEntities) and $this->nbt->TileEntities instanceof ListTag){ - $this->nbt->TileEntities->setTagType(NBT::TAG_Compound); - }else{ - $this->nbt->TileEntities = new ListTag("TileEntities", []); - $this->nbt->TileEntities->setTagType(NBT::TAG_Compound); - } - - if(isset($this->nbt->TileTicks) and $this->nbt->TileTicks instanceof ListTag){ - $this->nbt->TileTicks->setTagType(NBT::TAG_Compound); - }else{ - $this->nbt->TileTicks = new ListTag("TileTicks", []); - $this->nbt->TileTicks->setTagType(NBT::TAG_Compound); - } - - if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArrayTag)){ - $this->nbt->BiomeColors = new IntArrayTag("BiomeColors", array_fill(0, 256, 0)); - } - - if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArrayTag)){ - $this->nbt->HeightMap = new IntArrayTag("HeightMap", array_fill(0, 256, 0)); - } - - if(!isset($this->nbt->Blocks)){ - $this->nbt->Blocks = new ByteArrayTag("Blocks", str_repeat("\x00", 32768)); - } - - if(!isset($this->nbt->Data)){ - $this->nbt->Data = new ByteArrayTag("Data", $half = str_repeat("\x00", 16384)); - $this->nbt->SkyLight = new ByteArrayTag("SkyLight", $half); - $this->nbt->BlockLight = new ByteArrayTag("BlockLight", $half); - } - - $extraData = []; - - if(!isset($this->nbt->ExtraData) or !($this->nbt->ExtraData instanceof ByteArrayTag)){ - $this->nbt->ExtraData = new ByteArrayTag("ExtraData", Binary::writeInt(0)); - }else{ - $stream = new BinaryStream($this->nbt->ExtraData->getValue()); - $count = $stream->getInt(); - for($i = 0; $i < $count; ++$i){ - $key = $stream->getInt(); - $extraData[$key] = $stream->getShort(false); - } - } - - parent::__construct($level, $this->nbt["xPos"], $this->nbt["zPos"], $this->nbt->Blocks->getValue(), $this->nbt->Data->getValue(), $this->nbt->SkyLight->getValue(), $this->nbt->BlockLight->getValue(), $this->nbt->BiomeColors->getValue(), $this->nbt->HeightMap->getValue(), $this->nbt->Entities->getValue(), $this->nbt->TileEntities->getValue(), $extraData); - - if(isset($this->nbt->Biomes)){ - $this->checkOldBiomes($this->nbt->Biomes->getValue()); - unset($this->nbt->Biomes); - } - - unset($this->nbt->Blocks); - unset($this->nbt->Data); - unset($this->nbt->SkyLight); - unset($this->nbt->BlockLight); - unset($this->nbt->BiomeColors); - unset($this->nbt->HeightMap); - unset($this->nbt->Biomes); - } - - public function getBlockId($x, $y, $z){ - return ord($this->blocks{($x << 11) | ($z << 7) | $y}); - } - - public function setBlockId($x, $y, $z, $id){ - $this->blocks{($x << 11) | ($z << 7) | $y} = chr($id); - $this->hasChanged = true; - } - - public function getBlockData($x, $y, $z){ - $m = ord($this->data{($x << 10) | ($z << 6) | ($y >> 1)}); - if(($y & 1) === 0){ - return $m & 0x0F; - }else{ - return $m >> 4; - } - } - - public function setBlockData($x, $y, $z, $data){ - $i = ($x << 10) | ($z << 6) | ($y >> 1); - $old_m = ord($this->data{$i}); - if(($y & 1) === 0){ - $this->data{$i} = chr(($old_m & 0xf0) | ($data & 0x0f)); - }else{ - $this->data{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f)); - } - $this->hasChanged = true; - } - - public function getFullBlock($x, $y, $z){ - $i = ($x << 11) | ($z << 7) | $y; - if(($y & 1) === 0){ - return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) & 0x0F); - }else{ - return (ord($this->blocks{$i}) << 4) | (ord($this->data{$i >> 1}) >> 4); - } - } - - public function setBlock($x, $y, $z, $blockId = null, $meta = null){ - $i = ($x << 11) | ($z << 7) | $y; - - $changed = false; - - if($blockId !== null){ - $blockId = chr($blockId); - if($this->blocks{$i} !== $blockId){ - $this->blocks{$i} = $blockId; - $changed = true; - } - } - - if($meta !== null){ - $i >>= 1; - $old_m = ord($this->data{$i}); - if(($y & 1) === 0){ - $this->data{$i} = chr(($old_m & 0xf0) | ($meta & 0x0f)); - if(($old_m & 0x0f) !== $meta){ - $changed = true; - } - }else{ - $this->data{$i} = chr((($meta & 0x0f) << 4) | ($old_m & 0x0f)); - if((($old_m & 0xf0) >> 4) !== $meta){ - $changed = true; - } - } - } - - if($changed){ - $this->hasChanged = true; - } - - return $changed; - } - - public function getBlockSkyLight($x, $y, $z){ - $sl = ord($this->skyLight{($x << 10) | ($z << 6) | ($y >> 1)}); - if(($y & 1) === 0){ - return $sl & 0x0F; - }else{ - return $sl >> 4; - } - } - - public function setBlockSkyLight($x, $y, $z, $level){ - $i = ($x << 10) | ($z << 6) | ($y >> 1); - $old_sl = ord($this->skyLight{$i}); - if(($y & 1) === 0){ - $this->skyLight{$i} = chr(($old_sl & 0xf0) | ($level & 0x0f)); - }else{ - $this->skyLight{$i} = chr((($level & 0x0f) << 4) | ($old_sl & 0x0f)); - } - $this->hasChanged = true; - } - - public function getBlockLight($x, $y, $z){ - $l = ord($this->blockLight{($x << 10) | ($z << 6) | ($y >> 1)}); - if(($y & 1) === 0){ - return $l & 0x0F; - }else{ - return $l >> 4; - } - } - - public function setBlockLight($x, $y, $z, $level){ - $i = ($x << 10) | ($z << 6) | ($y >> 1); - $old_l = ord($this->blockLight{$i}); - if(($y & 1) === 0){ - $this->blockLight{$i} = chr(($old_l & 0xf0) | ($level & 0x0f)); - }else{ - $this->blockLight{$i} = chr((($level & 0x0f) << 4) | ($old_l & 0x0f)); - } - $this->hasChanged = true; - } - - public function getBlockIdColumn($x, $z){ - return substr($this->blocks, ($x << 11) + ($z << 7), 128); - } - - public function getBlockDataColumn($x, $z){ - return substr($this->data, ($x << 10) + ($z << 6), 64); - } - - public function getBlockSkyLightColumn($x, $z){ - return substr($this->skyLight, ($x << 10) + ($z << 6), 64); - } - - public function getBlockLightColumn($x, $z){ - return substr($this->blockLight, ($x << 10) + ($z << 6), 64); - } - - public function isLightPopulated(){ - return $this->nbt["LightPopulated"] > 0; - } - - public function setLightPopulated($value = 1){ - $this->nbt->LightPopulated = new ByteTag("LightPopulated", $value ? 1 : 0); - $this->hasChanged = true; - } - - /** - * @return bool - */ - public function isPopulated(){ - return isset($this->nbt->TerrainPopulated) and $this->nbt->TerrainPopulated->getValue() > 0; - } - - /** - * @param int $value - */ - public function setPopulated($value = 1){ - $this->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $value ? 1 : 0); - $this->hasChanged = true; - } - - /** - * @return bool - */ - public function isGenerated(){ - if(isset($this->nbt->TerrainGenerated)){ - return $this->nbt->TerrainGenerated->getValue() > 0; - }elseif(isset($this->nbt->TerrainPopulated)){ - return $this->nbt->TerrainPopulated->getValue() > 0; - } - return false; - } - - /** - * @param int $value - */ - public function setGenerated($value = 1){ - $this->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", (int) $value); - $this->hasChanged = true; - } - - /** - * @param string $data - * @param LevelProvider $provider - * - * @return Chunk - */ - public static function fromBinary($data, LevelProvider $provider = null){ - $nbt = new NBT(NBT::BIG_ENDIAN); - - try{ - $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); - $chunk = $nbt->getData(); - - if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ - return null; - } - - return new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, $chunk->Level); - }catch(\Throwable $e){ - return null; - } - } - - public static function fromFastBinary($data, LevelProvider $provider = null){ - - try{ - $offset = 0; - - $chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null); - $chunk->provider = $provider; - $chunk->x = Binary::readInt(substr($data, $offset, 4)); - $offset += 4; - $chunk->z = Binary::readInt(substr($data, $offset, 4)); - $offset += 4; - - $chunk->blocks = substr($data, $offset, 32768); - $offset += 32768; - $chunk->data = substr($data, $offset, 16384); - $offset += 16384; - $chunk->skyLight = substr($data, $offset, 16384); - $offset += 16384; - $chunk->blockLight = substr($data, $offset, 16384); - $offset += 16384; - - $chunk->heightMap = array_values(unpack("C*", substr($data, $offset, 256))); - $offset += 256; - $chunk->biomeColors = array_values(unpack("N*", substr($data, $offset, 1024))); - $offset += 1024; - - $flags = ord($data{$offset++}); - - $chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $flags & 0b1); - $chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", ($flags >> 1) & 0b1); - $chunk->nbt->LightPopulated = new ByteTag("LightPopulated", ($flags >> 2) & 0b1); - - return $chunk; - }catch(\Throwable $e){ - return null; - } - } - - public function toFastBinary(){ - return - Binary::writeInt($this->x) . - Binary::writeInt($this->z) . - $this->getBlockIdArray() . - $this->getBlockDataArray() . - $this->getBlockSkyLightArray() . - $this->getBlockLightArray() . - pack("C*", ...$this->getHeightMapArray()) . - pack("N*", ...$this->getBiomeColorArray()) . - chr(($this->isLightPopulated() ? 1 << 2 : 0) + ($this->isPopulated() ? 1 << 1 : 0) + ($this->isGenerated() ? 1 : 0)); - } - - public function toBinary(){ - $nbt = clone $this->getNBT(); - - $nbt->xPos = new IntTag("xPos", $this->x); - $nbt->zPos = new IntTag("zPos", $this->z); - - if($this->isGenerated()){ - $nbt->Blocks = new ByteArrayTag("Blocks", $this->getBlockIdArray()); - $nbt->Data = new ByteArrayTag("Data", $this->getBlockDataArray()); - $nbt->SkyLight = new ByteArrayTag("SkyLight", $this->getBlockSkyLightArray()); - $nbt->BlockLight = new ByteArrayTag("BlockLight", $this->getBlockLightArray()); - - $nbt->BiomeColors = new IntArrayTag("BiomeColors", $this->getBiomeColorArray()); - - $nbt->HeightMap = new IntArrayTag("HeightMap", $this->getHeightMapArray()); - } - - $entities = []; - - foreach($this->getEntities() as $entity){ - if(!($entity instanceof Player) and !$entity->closed){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new ListTag("Entities", $entities); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - - $tiles = []; - foreach($this->getTiles() as $tile){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - - $nbt->TileEntities = new ListTag("TileEntities", $tiles); - $nbt->TileEntities->setTagType(NBT::TAG_Compound); - - $extraData = new BinaryStream(); - $extraData->putInt(count($this->getBlockExtraDataArray())); - foreach($this->getBlockExtraDataArray() as $key => $value){ - $extraData->putInt($key); - $extraData->putShort($value); - } - - $nbt->ExtraData = new ByteArrayTag("ExtraData", $extraData->getBuffer()); - - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new CompoundTag("", ["Level" => $nbt])); - - return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); - } - - /** - * @return CompoundTag - */ - public function getNBT(){ - return $this->nbt; - } - - /** - * @param int $chunkX - * @param int $chunkZ - * @param LevelProvider $provider - * - * @return Chunk - */ - public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){ - try{ - $chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null); - $chunk->x = $chunkX; - $chunk->z = $chunkZ; - - $chunk->data = str_repeat("\x00", 16384); - $chunk->blocks = $chunk->data . $chunk->data; - $chunk->skyLight = str_repeat("\xff", 16384); - $chunk->blockLight = $chunk->data; - - $chunk->heightMap = array_fill(0, 256, 0); - $chunk->biomeColors = array_fill(0, 256, 0); - - $chunk->nbt->V = new ByteTag("V", 1); - $chunk->nbt->InhabitedTime = new LongTag("InhabitedTime", 0); - $chunk->nbt->TerrainGenerated = new ByteTag("TerrainGenerated", 0); - $chunk->nbt->TerrainPopulated = new ByteTag("TerrainPopulated", 0); - $chunk->nbt->LightPopulated = new ByteTag("LightPopulated", 0); - - return $chunk; - }catch(\Throwable $e){ - return null; - } - } -} \ No newline at end of file diff --git a/src/pocketmine/level/format/mcregion/McRegion.php b/src/pocketmine/level/format/mcregion/McRegion.php index 93d3a76cf..07c78dee6 100644 --- a/src/pocketmine/level/format/mcregion/McRegion.php +++ b/src/pocketmine/level/format/mcregion/McRegion.php @@ -21,26 +21,157 @@ namespace pocketmine\level\format\mcregion; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; +use pocketmine\level\format\LevelProvider; +use pocketmine\level\format\generic\GenericChunk; +use pocketmine\level\format\generic\SubChunk; use pocketmine\level\format\generic\BaseLevelProvider; use pocketmine\level\generator\Generator; use pocketmine\level\Level; use pocketmine\nbt\NBT; -use pocketmine\nbt\tag\ByteTag; -use pocketmine\nbt\tag\CompoundTag; -use pocketmine\nbt\tag\IntTag; -use pocketmine\nbt\tag\LongTag; -use pocketmine\nbt\tag\StringTag; -use pocketmine\tile\Spawnable; -use pocketmine\utils\BinaryStream; +use pocketmine\nbt\tag\{ByteArrayTag, ByteTag, CompoundTag, IntArrayTag, IntTag, ListTag, LongTag, StringTag}; +use pocketmine\Player; use pocketmine\utils\ChunkException; class McRegion extends BaseLevelProvider{ + + public static function nbtSerialize(GenericChunk $chunk) : string{ + $nbt = new CompoundTag("Level", []); + $nbt->xPos = new IntTag("xPos", $chunk->getX()); + $nbt->zPos = new IntTag("zPos", $chunk->getZ()); + + $nbt->V = new ByteTag("V", 0); //guess + $nbt->LastUpdate = new LongTag("LastUpdate", 0); //TODO + $nbt->InhabitedTime = new LongTag("InhabitedTime", 0); //TODO + $nbt->TerrainGenerated = new ByteTag("TerrainGenerated", $chunk->isGenerated()); + $nbt->TerrainPopulated = new ByteTag("TerrainPopulated", $chunk->isPopulated()); + $nbt->LightPopulated = new ByteTag("LightPopulated", $chunk->isLightPopulated()); + + $ids = ""; + $data = ""; + $blockLight = ""; + $skyLight = ""; + for($x = 0; $x < 16; ++$x){ + for($z = 0; $z < 16; ++$z){ + for($y = 0; $y < 8; ++$y){ + $subChunk = $chunk->getSubChunk($y); + $ids .= $subChunk->getBlockIdColumn($x, $z); + $data .= $subChunk->getBlockDataColumn($x, $z); + $blockLight .= $subChunk->getBlockLightColumn($x, $z); + $skyLight .= $subChunk->getSkyLightColumn($x, $z); + } + } + } + + $nbt->Blocks = new ByteArrayTag("Blocks", $ids); + $nbt->Data = new ByteArrayTag("Data", $data); + $nbt->SkyLight = new ByteArrayTag("SkyLight", $skyLight); + $nbt->BlockLight = new ByteArrayTag("BlockLight", $blockLight); + + $nbt->BiomeColors = new IntArrayTag("BiomeColors", $chunk->getBiomeColorArray()); + $nbt->HeightMap = new IntArrayTag("HeightMap", $chunk->getHeightMapArray()); + + $entities = []; + + foreach($chunk->getEntities() as $entity){ + if(!($entity instanceof Player) and !$entity->closed){ + $entity->saveNBT(); + $entities[] = $entity->namedtag; + } + } + + $nbt->Entities = new ListTag("Entities", $entities); + $nbt->Entities->setTagType(NBT::TAG_Compound); + + $tiles = []; + foreach($chunk->getTiles() as $tile){ + $tile->saveNBT(); + $tiles[] = $tile->namedtag; + } + + $nbt->TileEntities = new ListTag("TileEntities", $tiles); + $nbt->TileEntities->setTagType(NBT::TAG_Compound); + + //TODO: TileTicks + + $writer = new NBT(NBT::BIG_ENDIAN); + $nbt->setName("Level"); + $writer->setData(new CompoundTag("", ["Level" => $nbt])); + + return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); + } + + public static function nbtDeserialize(string $data, LevelProvider $provider = null){ + $nbt = new NBT(NBT::BIG_ENDIAN); + try{ + $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); + + $chunk = $nbt->getData(); + + if(!isset($chunk->Level) or !($chunk->Level instanceof CompoundTag)){ + return null; + } + + $chunk = $chunk->Level; + + $subChunks = []; + $fullIds = $chunk->Blocks instanceof ByteArrayTag ? $chunk->Blocks->getValue() : str_repeat("\x00", 32768); + $fullData = $chunk->Data instanceof ByteArrayTag ? $chunk->Data->getValue() : ($half = str_repeat("\x00", 16384)); + $fullBlockLight = $chunk->BlockLight instanceof ByteArrayTag ? $chunk->BlockLight->getValue() : $half; + $fullSkyLight = $chunk->SkyLight instanceof ByteArrayTag ? $chunk->SkyLight->getValue() : str_repeat("\xff", 16384); + + for($y = 0; $y < 8; ++$y){ + $offset = ($y << 4); + $ids = ""; + for($i = 0; $i < 256; ++$i){ + $ids .= substr($fullIds, $offset, 16); + $offset += 128; + } + $data = ""; + $offset = ($y << 3); + for($i = 0; $i < 256; ++$i){ + $data .= substr($fullData, $offset, 8); + $offset += 64; + } + $blockLight = ""; + $offset = ($y << 3); + for($i = 0; $i < 256; ++$i){ + $blockLight .= substr($fullBlockLight, $offset, 8); + $offset += 64; + } + $skyLight = ""; + $offset = ($y << 3); + for($i = 0; $i < 256; ++$i){ + $skyLight .= substr($fullSkyLight, $offset, 8); + $offset += 64; + } + $subChunks[] = new SubChunk($y, $ids, $data, $blockLight, $skyLight); + } + + $result = new GenericChunk( + $provider, + $chunk["xPos"], + $chunk["zPos"], + $subChunks, + $chunk->Entities instanceof ListTag ? $chunk->Entities->getValue() : [], + $chunk->TileEntities instanceof ListTag ? $chunk->TileEntities->getValue() : [], + $chunk->BiomeColors instanceof IntArrayTag ? $chunk->BiomeColors->getValue() : [], + $chunk->HeightMap instanceof IntArrayTag ? $chunk->HeightMap->getValue() : [] + ); + $result->setLightPopulated($chunk->LightPopulated instanceof ByteTag ? ((bool) $chunk->LightPopulated->getValue()) : false); + $result->setPopulated($chunk->TerrainPopulated instanceof ByteTag ? ((bool) $chunk->TerrainPopulated->getValue()) : false); + $result->setGenerated($chunk->TerrainGenerated instanceof ByteTag ? ((bool) $chunk->TerrainGenerated->getValue()) : false); + return $result; + }catch(\Throwable $e){ + echo $e->getMessage(); + return null; + } + } /** @var RegionLoader[] */ protected $regions = []; - /** @var Chunk[] */ + /** @var GenericChunk[] */ protected $chunks = []; public static function getProviderName(){ @@ -51,10 +182,6 @@ class McRegion extends BaseLevelProvider{ return self::ORDER_ZXY; } - public static function usesChunkSection(){ - return false; - } - public static function isValid($path){ $isValid = (file_exists($path . "/level.dat") and is_dir($path . "/region/")); @@ -71,6 +198,11 @@ class McRegion extends BaseLevelProvider{ return $isValid; } + public function getWorldHeight() : int{ + //TODO: add world height options + return 128; + } + public static function generate($path, $name, $seed, $generator, array $options = []){ if(!file_exists($path)){ mkdir($path, 0777, true); @@ -112,47 +244,6 @@ class McRegion extends BaseLevelProvider{ $z = $chunkZ >> 5; } - public function requestChunkTask($x, $z){ - $chunk = $this->getChunk($x, $z, false); - if(!($chunk instanceof Chunk)){ - throw new ChunkException("Invalid Chunk sent"); - } - - $tiles = ""; - - if(count($chunk->getTiles()) > 0){ - $nbt = new NBT(NBT::LITTLE_ENDIAN); - $list = []; - foreach($chunk->getTiles() as $tile){ - if($tile instanceof Spawnable){ - $list[] = $tile->getSpawnCompound(); - } - } - $nbt->setData($list); - $tiles = $nbt->write(true); - } - - $extraData = new BinaryStream(); - $extraData->putLInt(count($chunk->getBlockExtraDataArray())); - foreach($chunk->getBlockExtraDataArray() as $key => $value){ - $extraData->putLInt($key); - $extraData->putLShort($value); - } - - $ordered = $chunk->getBlockIdArray() . - $chunk->getBlockDataArray() . - $chunk->getBlockSkyLightArray() . - $chunk->getBlockLightArray() . - pack("C*", ...$chunk->getHeightMapArray()) . - pack("N*", ...$chunk->getBiomeColorArray()) . - $extraData->getBuffer() . - $tiles; - - $this->getLevel()->chunkRequestCallback($x, $z, $ordered); - - return null; - } - public function unloadChunks(){ foreach($this->chunks as $chunk){ $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); @@ -216,12 +307,12 @@ class McRegion extends BaseLevelProvider{ } public function getEmptyChunk($chunkX, $chunkZ){ - return Chunk::getEmptyChunk($chunkX, $chunkZ, $this); + return GenericChunk::getEmptyChunk($chunkX, $chunkZ, $this); } public function unloadChunk($x, $z, $safe = true){ $chunk = isset($this->chunks[$index = Level::chunkHash($x, $z)]) ? $this->chunks[$index] : null; - if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){ + if($chunk instanceof Chunk and $chunk->unload(false, $safe)){ unset($this->chunks[$index]); return true; } @@ -246,7 +337,7 @@ class McRegion extends BaseLevelProvider{ * @return RegionLoader */ protected function getRegion($x, $z){ - return isset($this->regions[$index = Level::chunkHash($x, $z)]) ? $this->regions[$index] : null; + return $this->regions[Level::chunkHash($x, $z)] ?? null; } /** @@ -267,8 +358,8 @@ class McRegion extends BaseLevelProvider{ } } - public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ - if(!($chunk instanceof Chunk)){ + public function setChunk($chunkX, $chunkZ, Chunk $chunk){ + if(!($chunk instanceof GenericChunk)){ throw new ChunkException("Invalid Chunk class"); } @@ -288,10 +379,6 @@ class McRegion extends BaseLevelProvider{ $this->chunks[$index] = $chunk; } - public static function createChunkSection($Y){ - return null; - } - public function isChunkGenerated($chunkX, $chunkZ){ if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){ return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32) and $this->getChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32, true)->isGenerated(); diff --git a/src/pocketmine/level/format/mcregion/RegionLoader.php b/src/pocketmine/level/format/mcregion/RegionLoader.php index 1b4a7eb76..acfa9823a 100644 --- a/src/pocketmine/level/format/mcregion/RegionLoader.php +++ b/src/pocketmine/level/format/mcregion/RegionLoader.php @@ -21,7 +21,7 @@ namespace pocketmine\level\format\mcregion; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\generic\GenericChunk; use pocketmine\level\format\LevelProvider; use pocketmine\utils\Binary; use pocketmine\utils\ChunkException; @@ -112,7 +112,7 @@ class RegionLoader{ } $chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1)); - if($chunk instanceof FullChunk){ + if($chunk instanceof GenericChunk){ return $chunk; }else{ MainLogger::getLogger()->error("Corrupted chunk detected"); @@ -121,7 +121,7 @@ class RegionLoader{ } protected function unserializeChunk($data){ - return Chunk::fromBinary($data, $this->levelProvider); + return McRegion::nbtDeserialize($data, $this->levelProvider); } public function chunkExists($x, $z){ @@ -161,9 +161,9 @@ class RegionLoader{ $this->locationTable[$index][1] = 0; } - public function writeChunk(FullChunk $chunk){ + public function writeChunk(GenericChunk $chunk){ $this->lastUsed = time(); - $chunkData = $chunk->toBinary(); + $chunkData = McRegion::nbtSerialize($chunk); if($chunkData !== false){ $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunkData); } diff --git a/src/pocketmine/level/generator/Flat.php b/src/pocketmine/level/generator/Flat.php index a77cb5364..f9eb7ecfd 100644 --- a/src/pocketmine/level/generator/Flat.php +++ b/src/pocketmine/level/generator/Flat.php @@ -31,7 +31,7 @@ use pocketmine\block\LapisOre; use pocketmine\block\RedstoneOre; use pocketmine\item\Item; use pocketmine\level\ChunkManager; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\level\generator\biome\Biome; use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Populator; @@ -41,7 +41,7 @@ use pocketmine\utils\Random; class Flat extends Generator{ /** @var ChunkManager */ private $level; - /** @var FullChunk */ + /** @var Chunk */ private $chunk; /** @var Random */ private $random; diff --git a/src/pocketmine/level/generator/GenerationTask.php b/src/pocketmine/level/generator/GenerationTask.php index d995b73eb..fd8001209 100644 --- a/src/pocketmine/level/generator/GenerationTask.php +++ b/src/pocketmine/level/generator/GenerationTask.php @@ -21,7 +21,8 @@ namespace pocketmine\level\generator; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; +use pocketmine\level\format\generic\GenericChunk; use pocketmine\level\Level; use pocketmine\level\SimpleChunkManager; use pocketmine\scheduler\AsyncTask; @@ -34,10 +35,10 @@ class GenerationTask extends AsyncTask{ public $chunk; public $chunkClass; - public function __construct(Level $level, FullChunk $chunk){ + public function __construct(Level $level, Chunk $chunk){ $this->state = true; $this->levelId = $level->getId(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastSerialize($chunk); $this->chunkClass = get_class($chunk); } @@ -51,9 +52,9 @@ class GenerationTask extends AsyncTask{ return; } - /** @var FullChunk $chunk */ + /** @var Chunk $chunk */ $chunk = $this->chunkClass; - $chunk = $chunk::fromFastBinary($this->chunk); + $chunk = GenericChunk::fastDeserialize($this->chunk); if($chunk === null){ //TODO error return; @@ -65,7 +66,7 @@ class GenerationTask extends AsyncTask{ $chunk = $manager->getChunk($chunk->getX(), $chunk->getZ()); $chunk->setGenerated(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastSerialize($chunk); $manager->setChunk($chunk->getX(), $chunk->getZ(), null); } @@ -77,9 +78,9 @@ class GenerationTask extends AsyncTask{ $level->registerGenerator(); return; } - /** @var FullChunk $chunk */ + /** @var Chunk $chunk */ $chunk = $this->chunkClass; - $chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider()); + $chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error return; diff --git a/src/pocketmine/level/generator/LightPopulationTask.php b/src/pocketmine/level/generator/LightPopulationTask.php index ffa98b947..d2acffe1c 100644 --- a/src/pocketmine/level/generator/LightPopulationTask.php +++ b/src/pocketmine/level/generator/LightPopulationTask.php @@ -21,7 +21,8 @@ namespace pocketmine\level\generator; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; +use pocketmine\level\format\generic\FullChunk; use pocketmine\level\Level; use pocketmine\scheduler\AsyncTask; use pocketmine\Server; @@ -32,16 +33,16 @@ class LightPopulationTask extends AsyncTask{ public $chunk; public $chunkClass; - public function __construct(Level $level, FullChunk $chunk){ + public function __construct(Level $level, Chunk $chunk){ $this->levelId = $level->getId(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastDeserialize($this->chunk); $this->chunkClass = get_class($chunk); } public function onRun(){ - /** @var FullChunk $chunk */ + /** @var Chunk $chunk */ $chunk = $this->chunkClass; - $chunk = $chunk::fromFastBinary($this->chunk); + $chunk = GenericChunk::fastDeserialize($this->chunk); if($chunk === null){ //TODO error return; @@ -51,15 +52,15 @@ class LightPopulationTask extends AsyncTask{ $chunk->populateSkyLight(); $chunk->setLightPopulated(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastSerialize($chunk); } public function onCompletion(Server $server){ $level = $server->getLevel($this->levelId); if($level !== null){ - /** @var FullChunk $chunk */ + /** @var Chunk $chunk */ $chunk = $this->chunkClass; - $chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider()); + $chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error return; diff --git a/src/pocketmine/level/generator/PopulationTask.php b/src/pocketmine/level/generator/PopulationTask.php index a74acdeb4..869a5fa85 100644 --- a/src/pocketmine/level/generator/PopulationTask.php +++ b/src/pocketmine/level/generator/PopulationTask.php @@ -21,7 +21,8 @@ namespace pocketmine\level\generator; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; +use pocketmine\level\format\generic\GenericChunk; use pocketmine\level\Level; use pocketmine\level\SimpleChunkManager; use pocketmine\scheduler\AsyncTask; @@ -29,11 +30,9 @@ use pocketmine\Server; class PopulationTask extends AsyncTask{ - public $state; public $levelId; public $chunk; - public $chunkClass; public $chunk0; public $chunk1; @@ -45,11 +44,10 @@ class PopulationTask extends AsyncTask{ public $chunk7; public $chunk8; - public function __construct(Level $level, FullChunk $chunk){ + public function __construct(Level $level, Chunk $chunk){ $this->state = true; $this->levelId = $level->getId(); - $this->chunk = $chunk->toFastBinary(); - $this->chunkClass = get_class($chunk); + $this->chunk = GenericChunk::fastSerialize($chunk); for($i = 0; $i < 9; ++$i){ if($i === 4){ @@ -58,7 +56,7 @@ class PopulationTask extends AsyncTask{ $xx = -1 + $i % 3; $zz = -1 + (int) ($i / 3); $ck = $level->getChunk($chunk->getX() + $xx, $chunk->getZ() + $zz, false); - $this->{"chunk$i"} = $ck !== null ? $ck->toFastBinary() : null; + $this->{"chunk$i"} = $ck !== null ? GenericChunk::fastSerialize($ck) : null; } } @@ -72,12 +70,10 @@ class PopulationTask extends AsyncTask{ return; } - /** @var FullChunk[] $chunks */ + /** @var Chunk[] $chunks */ $chunks = []; - /** @var FullChunk $chunkC */ - $chunkC = $this->chunkClass; - $chunk = $chunkC::fromFastBinary($this->chunk); + $chunk = GenericChunk::fastDeserialize($this->chunk); for($i = 0; $i < 9; ++$i){ if($i === 4){ @@ -87,9 +83,9 @@ class PopulationTask extends AsyncTask{ $zz = -1 + (int) ($i / 3); $ck = $this->{"chunk$i"}; if($ck === null){ - $chunks[$i] = $chunkC::getEmptyChunk($chunk->getX() + $xx, $chunk->getZ() + $zz); + $chunks[$i] = GenericChunk::getEmptyChunk($chunk->getX() + $xx, $chunk->getZ() + $zz); }else{ - $chunks[$i] = $chunkC::fromFastBinary($ck); + $chunks[$i] = GenericChunk::fastDeserialize($ck); } } @@ -122,7 +118,7 @@ class PopulationTask extends AsyncTask{ $chunk->populateSkyLight(); $chunk->setLightPopulated(); $chunk->setPopulated(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = GenericChunk::fastSerialize($chunk); $manager->setChunk($chunk->getX(), $chunk->getZ(), null); @@ -145,7 +141,7 @@ class PopulationTask extends AsyncTask{ continue; } - $this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->toFastBinary() : null; + $this->{"chunk$i"} = $chunks[$i] !== null ? GenericChunk::fastSerialize($chunks[$i]) : null; } } @@ -157,10 +153,7 @@ class PopulationTask extends AsyncTask{ return; } - /** @var FullChunk $chunkC */ - $chunkC = $this->chunkClass; - - $chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider()); + $chunk = GenericChunk::fastDeserialize($this->chunk, $level->getProvider()); if($chunk === null){ //TODO error @@ -173,7 +166,7 @@ class PopulationTask extends AsyncTask{ } $c = $this->{"chunk$i"}; if($c !== null){ - $c = $chunkC::fromFastBinary($c, $level->getProvider()); + $c = GenericChunk::fastDeserialize($c, $level->getProvider()); $level->generateChunkCallback($c->getX(), $c->getZ(), $c); } } diff --git a/src/pocketmine/tile/Chest.php b/src/pocketmine/tile/Chest.php index c924482e6..ba3e3fbe5 100644 --- a/src/pocketmine/tile/Chest.php +++ b/src/pocketmine/tile/Chest.php @@ -25,7 +25,7 @@ use pocketmine\inventory\ChestInventory; use pocketmine\inventory\DoubleChestInventory; use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\math\Vector3; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; @@ -40,7 +40,7 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{ /** @var DoubleChestInventory */ protected $doubleInventory = null; - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ parent::__construct($chunk, $nbt); $this->inventory = new ChestInventory($this); diff --git a/src/pocketmine/tile/FlowerPot.php b/src/pocketmine/tile/FlowerPot.php index 099ceaeef..2a2d33668 100644 --- a/src/pocketmine/tile/FlowerPot.php +++ b/src/pocketmine/tile/FlowerPot.php @@ -22,7 +22,7 @@ namespace pocketmine\tile; use pocketmine\item\Item; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\ShortTag; @@ -30,7 +30,7 @@ use pocketmine\nbt\tag\StringTag; class FlowerPot extends Spawnable{ - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->Item)){ $nbt->item = new ShortTag("item", 0); } diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index ba7e747e9..badf8bf0e 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -28,7 +28,7 @@ use pocketmine\inventory\FurnaceInventory; use pocketmine\inventory\FurnaceRecipe; use pocketmine\inventory\InventoryHolder; use pocketmine\item\Item; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; @@ -41,7 +41,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ /** @var FurnaceInventory */ protected $inventory; - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->BurnTime) or $nbt["BurnTime"] < 0){ $nbt->BurnTime = new ShortTag("BurnTime", 0); } diff --git a/src/pocketmine/tile/Sign.php b/src/pocketmine/tile/Sign.php index b3f2c63c7..3ec735630 100644 --- a/src/pocketmine/tile/Sign.php +++ b/src/pocketmine/tile/Sign.php @@ -21,14 +21,14 @@ namespace pocketmine\tile; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; class Sign extends Spawnable{ - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->Text1)){ $nbt->Text1 = new StringTag("Text1", ""); } diff --git a/src/pocketmine/tile/Skull.php b/src/pocketmine/tile/Skull.php index bd17209ee..a65cb11a2 100644 --- a/src/pocketmine/tile/Skull.php +++ b/src/pocketmine/tile/Skull.php @@ -21,7 +21,7 @@ namespace pocketmine\tile; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\IntTag; @@ -34,7 +34,7 @@ class Skull extends Spawnable{ const TYPE_HUMAN = 3; const TYPE_CREEPER = 4; - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ if(!isset($nbt->SkullType)){ $nbt->SkullType = new ByteTag("SkullType", 0); } diff --git a/src/pocketmine/tile/Spawnable.php b/src/pocketmine/tile/Spawnable.php index f9fb98956..b76f0a164 100644 --- a/src/pocketmine/tile/Spawnable.php +++ b/src/pocketmine/tile/Spawnable.php @@ -21,7 +21,7 @@ namespace pocketmine\tile; -use pocketmine\level\format\FullChunk; +use pocketmine\level\format\Chunk; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\protocol\BlockEntityDataPacket; @@ -51,7 +51,7 @@ abstract class Spawnable extends Tile{ */ public abstract function getSpawnCompound(); - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ parent::__construct($chunk, $nbt); $this->spawnToAll(); } diff --git a/src/pocketmine/tile/Tile.php b/src/pocketmine/tile/Tile.php index aa9ef2ff3..cbe2d0300 100644 --- a/src/pocketmine/tile/Tile.php +++ b/src/pocketmine/tile/Tile.php @@ -26,7 +26,6 @@ namespace pocketmine\tile; use pocketmine\event\Timings; use pocketmine\level\format\Chunk; -use pocketmine\level\format\FullChunk; use pocketmine\level\Level; use pocketmine\level\Position; use pocketmine\nbt\tag\CompoundTag; @@ -68,13 +67,13 @@ abstract class Tile extends Position{ /** * @param string $type - * @param FullChunk $chunk + * @param Chunk $chunk * @param CompoundTag $nbt * @param $args * * @return Tile */ - public static function createTile($type, FullChunk $chunk, CompoundTag $nbt, ...$args){ + public static function createTile($type, Chunk $chunk, CompoundTag $nbt, ...$args){ if(isset(self::$knownTiles[$type])){ $class = self::$knownTiles[$type]; return new $class($chunk, $nbt, ...$args); @@ -108,7 +107,7 @@ abstract class Tile extends Position{ return self::$shortNames[static::class]; } - public function __construct(FullChunk $chunk, CompoundTag $nbt){ + public function __construct(Chunk $chunk, CompoundTag $nbt){ assert($chunk !== null and $chunk->getProvider() !== null); $this->timings = Timings::getTileEntityTimings($this); @@ -163,7 +162,7 @@ abstract class Tile extends Position{ if(!$this->closed){ $this->closed = true; unset($this->level->updateTiles[$this->id]); - if($this->chunk instanceof FullChunk){ + if($this->chunk instanceof Chunk){ $this->chunk->removeTile($this); } if(($level = $this->getLevel()) instanceof Level){