From 95b59793510099361626643aa441a0b3b9dd334c Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Thu, 31 Jul 2014 14:28:17 +0200 Subject: [PATCH] Removed SimpleChunk from generator, core, use binary representation of chunks --- src/pocketmine/PocketMine.php | 2 +- src/pocketmine/Server.php | 1 - src/pocketmine/level/Level.php | 41 +-- src/pocketmine/level/format/ChunkSnapshot.php | 93 ------- src/pocketmine/level/format/FullChunk.php | 31 ++- src/pocketmine/level/format/LevelProvider.php | 10 +- src/pocketmine/level/format/SimpleChunk.php | 259 ------------------ src/pocketmine/level/format/anvil/Anvil.php | 40 +-- src/pocketmine/level/format/anvil/Chunk.php | 107 ++++++-- .../level/format/anvil/ChunkRequestTask.php | 5 +- .../level/format/anvil/ChunkSnapshot.php | 78 ------ .../level/format/anvil/RegionLoader.php | 60 +--- .../level/format/generic/BaseChunk.php | 87 +++--- .../format/generic/BaseChunkSnapshot.php | 64 ----- .../level/format/generic/BaseFullChunk.php | 82 +++--- .../level/format/mcregion/Chunk.php | 76 ++++- .../format/mcregion/ChunkRequestTask.php | 5 +- .../level/format/mcregion/ChunkSnapshot.php | 78 ------ .../level/format/mcregion/McRegion.php | 43 +-- .../level/format/mcregion/RegionLoader.php | 56 +--- src/pocketmine/level/generator/Flat.php | 20 +- .../generator/GenerationChunkManager.php | 53 ++-- .../level/generator/GenerationManager.php | 30 +- .../generator/GenerationRequestManager.php | 19 +- .../level/generator/GenerationThread.php | 1 + 25 files changed, 393 insertions(+), 948 deletions(-) delete mode 100644 src/pocketmine/level/format/ChunkSnapshot.php delete mode 100644 src/pocketmine/level/format/SimpleChunk.php delete mode 100644 src/pocketmine/level/format/anvil/ChunkSnapshot.php delete mode 100644 src/pocketmine/level/format/generic/BaseChunkSnapshot.php delete mode 100644 src/pocketmine/level/format/mcregion/ChunkSnapshot.php diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index e277db86c..3294804ac 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -141,7 +141,7 @@ namespace pocketmine { } } - gc_disable(); + gc_enable(); error_reporting(-1); ini_set("allow_url_fopen", 1); ini_set("display_errors", 1); diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 07affa1dd..0cadf4225 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1830,7 +1830,6 @@ class Server{ $lastLoop = 0; $connectionTimer = Timings::$connectionTimer; while($this->isRunning){ - $connectionTimer->startTiming(); foreach($this->interfaces as $interface){ if($interface->process()){ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 0bc901c26..75e90532b 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -40,7 +40,6 @@ use pocketmine\level\format\Chunk; use pocketmine\level\format\FullChunk; use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\format\LevelProvider; -use pocketmine\level\format\SimpleChunk; use pocketmine\level\generator\Generator; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector2; @@ -1292,51 +1291,17 @@ class Level implements ChunkManager, Metadatable{ return isset($this->chunks[$index = "$x:$z"]) ? $this->chunks[$index] : $this->chunks[$index] = $this->provider->getChunk($x, $z, $create); } - /** - * @param int $x - * @param int $z - * @param bool $create - * - * @return SimpleChunk - */ - public function getChunk($x, $z, $create = false){ - $chunk = $this->getChunkAt($x, $z, $create); - if($chunk === null){ - return new SimpleChunk($x, $z, 0); - }else{ - $flags = SimpleChunk::FLAG_GENERATED; - if($this->isChunkPopulated($x, $z)){ - $flags |= SimpleChunk::FLAG_POPULATED; - } - $blockIds = []; - $data = []; - - if(!$this->useSections){ - //TODO - return new SimpleChunk($x, $z, 0); - } - - for($Y = 0; $Y < 8; ++$Y){ - $section = $chunk->getSection($Y); - $blockIds[$Y] = $section->getIdArray(); - $data[$Y] = $section->getDataArray(); - } - - return new SimpleChunk($x, $z, $flags, $blockIds, $data); - } - } - - public function generateChunkCallback($x, $z, SimpleChunk $chunk){ + public function generateChunkCallback($x, $z, FullChunk $chunk){ unset($this->chunkGenerationQueue["$x:$z"]); $this->setChunk($x, $z, $chunk); } - public function setChunk($x, $z, SimpleChunk $chunk){ + public function setChunk($x, $z, FullChunk $chunk){ $index = Level::chunkHash($x, $z); foreach($this->getUsingChunk($x, $z) as $player){ $player->unloadChunk($x, $z); } - unset($this->chunks[Level::chunkHash($x, $z)]); + unset($this->chunks[$index]); $this->provider->setChunk($x, $z, $chunk); $this->loadChunk($x, $z); } diff --git a/src/pocketmine/level/format/ChunkSnapshot.php b/src/pocketmine/level/format/ChunkSnapshot.php deleted file mode 100644 index 9a027c672..000000000 --- a/src/pocketmine/level/format/ChunkSnapshot.php +++ /dev/null @@ -1,93 +0,0 @@ -x = $chunkX; - $this->z = $chunkZ; - $this->flags = $flags; - for($y = 0; $y < self::$HEIGHT; ++$y){ - $this->ids[$y] = (isset($ids[$y]) and strlen($ids[$y]) === 4096) ? $ids[$y] : str_repeat("\x00", 4096); - $this->meta[$y] = (isset($meta[$y]) and strlen($meta[$y]) === 2048) ? $meta[$y] : str_repeat("\x00", 2048); - } - } - - public function hasChanged($set = true){ - if($this->changed === true and $set === true){ - $this->changed = false; - return true; - } - - return $this->changed; - } - - /** - * @return int - */ - public function getX(){ - return $this->x; - } - - /** - * @return int - */ - public function getZ(){ - return $this->z; - } - - /** - * @param int $x - */ - public function setX($x){ - $this->x = $x; - } - - /** - * @param int $z - */ - public function setZ($z){ - $this->z = $z; - } - - /** - * @return bool - */ - public function isGenerated(){ - return ($this->flags & self::FLAG_GENERATED) > 0; - } - - /** - * @return bool - */ - public function isPopulated(){ - return ($this->flags & self::FLAG_POPULATED) > 0; - } - - /** - * @param bool $value - */ - public function setGenerated($value = true){ - $this->changed = true; - $this->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0); - } - - /** - * @param bool $value - */ - public function setPopulated($value = true){ - $this->changed = true; - $this->flags = ($this->flags & ~self::FLAG_POPULATED) | ($value === true ? self::FLAG_POPULATED : 0); - } - - /** - * @param int $x 0-15 - * @param int $y 0-127 - * @param int $z 0-15 - * - * @return int 0-255 - */ - public function getBlockId($x, $y, $z){ - return ord(@$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x}); - } - - /** - * @param int $x 0-15 - * @param int $y 0-127 - * @param int $z 0-15 - * @param int $blockId 0-255 - */ - public function setBlockId($x, $y, $z, $blockId){ - $this->changed = true; - @$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x} = chr($blockId); - } - - /** - * @param int $x 0-15 - * @param int $y 0-127 - * @param int $z 0-15 - * - * @return int 0-15 - */ - public function getBlockData($x, $y, $z){ - $m = ord($this->meta[$y >> 4]{(($y & 0x0f) << 7) + ($z << 3) + ($x >> 1)}); - if(($x & 1) === 0){ - return $m & 0x0F; - }else{ - return $m >> 4; - } - } - - /** - * @param int $x 0-15 - * @param int $y 0-127 - * @param int $z 0-15 - * @param int $data 0-15 - */ - public function setBlockData($x, $y, $z, $data){ - $this->changed = true; - $i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1); - $old_m = ord($this->meta[$y >> 4]{$i}); - if(($x & 1) === 0){ - $this->meta[$y >> 4]{$i} = chr(($old_m & 0xf0) | ($data & 0x0f)); - }else{ - $this->meta[$y >> 4]{$i} = chr((($data & 0x0f) << 4) | ($old_m & 0x0f)); - } - } - - /** - * @param int $y 0-7 - * - * @return string - */ - public function getSectionIds($y){ - return $this->ids[$y]; - } - - /** - * @param int $y 0-7 - * - * @return string - */ - public function getSectionData($y){ - return $this->meta[$y]; - } - - /** - * @param int $y 0-7 - * @param string $ids - * @param string $meta - */ - public function setSection($y, $ids = null, $meta = null){ - $this->changed = true; - if($ids !== null){ - $this->ids[$y] = $ids; - } - - if($meta !== null){ - $this->meta[$y] = $meta; - } - } - - /** - * @return string - */ - public function toBinary(){ - $binary = Binary::writeInt($this->x) . Binary::writeInt($this->z) . chr($this->flags); - if($this->isGenerated()){ - for($y = 0; $y < self::$HEIGHT; ++$y){ - $binary .= $this->getSectionIds($y); - $binary .= $this->getSectionData($y); - } - } - - return $binary; - } - - /** - * @param string $binary - * - * @return SimpleChunk - */ - public static function fromBinary($binary){ - $offset = 0; - $chunkX = Binary::readInt(substr($binary, $offset, 4)); - $offset += 4; - $chunkZ = Binary::readInt(substr($binary, $offset, 4)); - $offset += 4; - $flags = ord($binary{$offset++}); - $ids = []; - $meta = []; - if(($flags & self::FLAG_GENERATED) > 0){ - for($y = 0; $y < self::$HEIGHT; ++$y){ - $ids[$y] = substr($binary, $offset, 4096); - $offset += 4096; - $meta[$y] = substr($binary, $offset, 2048); - $offset += 2048; - } - } - - return new SimpleChunk($chunkX, $chunkZ, $flags, $ids, $meta); - } - -} \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/Anvil.php b/src/pocketmine/level/format/anvil/Anvil.php index 1a87d89db..9781feb9d 100644 --- a/src/pocketmine/level/format/anvil/Anvil.php +++ b/src/pocketmine/level/format/anvil/Anvil.php @@ -21,19 +21,12 @@ namespace pocketmine\level\format\anvil; -use pocketmine\level\format\generic\BaseLevelProvider; +use pocketmine\level\format\FullChunk; use pocketmine\level\format\mcregion\McRegion; -use pocketmine\level\format\SimpleChunk; -use pocketmine\level\generator\Generator; use pocketmine\level\Level; -use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\ByteArray; use pocketmine\nbt\tag\Compound; -use pocketmine\nbt\tag\Int; -use pocketmine\nbt\tag\Long; -use pocketmine\nbt\tag\String; -use pocketmine\Player; class Anvil extends McRegion{ @@ -105,8 +98,12 @@ class Anvil extends McRegion{ return parent::getChunk($chunkX, $chunkZ, $create); } - public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){ - if($chunk->isGenerated() === false){ + public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ + if(!($chunk instanceof Chunk)){ + throw new \Exception("Invalid Chunk class"); + } + + if($chunk->isPopulated() === false){ $this->unloadChunk($chunkX, $chunkZ, false); $regionX = $regionZ = null; self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); @@ -115,26 +112,15 @@ class Anvil extends McRegion{ $region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32); $this->loadChunk($chunkX, $chunkZ); }else{ - $newChunk = $this->getChunk($chunkX, $chunkZ, true); - for($y = 0; $y < 8; ++$y){ - $section = new ChunkSection(new Compound(null, [ - "Y" => new Byte("Y", $y), - "Blocks" => new ByteArray("Blocks", $chunk->getSectionIds($y)), - "Data" => new ByteArray("Data", $chunk->getSectionData($y)), - "SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO - "BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO - ])); - $newChunk->setSection($y, $section); - } - if($chunk->isPopulated()){ - $newChunk->setPopulated(1); - } + $newChunk = clone $chunk; + $newChunk->setX($chunkX); + $newChunk->setZ($chunkZ); $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $newChunk; - $this->saveChunk($chunkX, $chunkZ); + //$this->saveChunk($chunkX, $chunkZ); } } - public function createChunkSection($Y){ + public static function createChunkSection($Y){ return new ChunkSection(new Compound(null, [ "Y" => new Byte("Y", $Y), "Blocks" => new ByteArray("Blocks", str_repeat("\xff", 4096)), @@ -146,7 +132,7 @@ class Anvil extends McRegion{ public function isChunkGenerated($chunkX, $chunkZ){ if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){ - return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32); + 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; diff --git a/src/pocketmine/level/format/anvil/Chunk.php b/src/pocketmine/level/format/anvil/Chunk.php index 33a956171..77125e97f 100644 --- a/src/pocketmine/level/format/anvil/Chunk.php +++ b/src/pocketmine/level/format/anvil/Chunk.php @@ -30,6 +30,7 @@ use pocketmine\nbt\tag\ByteArray; use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\IntArray; +use pocketmine\Player; use pocketmine\utils\Binary; class Chunk extends BaseChunk{ @@ -37,7 +38,7 @@ class Chunk extends BaseChunk{ /** @var Compound */ protected $nbt; - public function __construct(LevelProvider $level, Compound $nbt){ + public function __construct($level, Compound $nbt){ $this->nbt = $nbt; if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){ @@ -108,35 +109,18 @@ class Chunk extends BaseChunk{ $this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value); } - public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){ - $blockId = ""; - $blockData = ""; - $blockSkyLight = ""; - $blockLight = ""; - $emptySections = [false, false, false, false, false, false, false, false]; + /** + * @return bool + */ + public function isGenerated(){ + return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0); + } - $emptyBlocks = str_repeat("\x00", 4096); - $emptyHalf = str_repeat("\x00", 2048); - - foreach($this->sections as $i => $section){ - if($section instanceof EmptyChunkSection){ - $blockId .= $emptyBlocks; - $blockData .= $emptyHalf; - $blockSkyLight .= $emptyHalf; - $blockLight .= $emptyHalf; - $emptySections[$i] = true; - }else{ - $blockId .= $section->getIdArray(); - $blockData .= $section->getDataArray(); - $blockSkyLight .= $section->getSkyLightArray(); - $blockLight .= $section->getLightArray(); - } - } - - //TODO: maxBlockY, biomeMap, biomeTemp - - //TODO: time - return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0 /*$this->getLevel()->getTime()*/, $blockId, $blockData, $blockSkyLight, $blockLight, $emptySections, null, null, null, null); + /** + * @param int $value + */ + public function setGenerated($value = 1){ + $this->nbt->TerrainGenerated = new Byte("TerrainGenerated", $value); } /** @@ -145,4 +129,69 @@ class Chunk extends BaseChunk{ 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); + $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); + $chunk = $nbt->getData(); + + if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){ + return null; + } + + return new Chunk($provider instanceof LevelProvider ? $provider : "pocketmine\\level\\format\\anvil\\Anvil", $chunk->Level); + } + + public function toBinary(){ + $nbt = $this->getNBT(); + + $nbt->Sections = new Enum("Sections", []); + $nbt->Sections->setTagType(NBT::TAG_Compound); + foreach($this->getSections() as $section){ + $nbt->Sections[$section->getY()] = new Compound(null, [ + "Y" => new Byte("Y", $section->getY()), + "Blocks" => new ByteArray("Blocks", $section->getIdArray()), + "Data" => new ByteArray("Data", $section->getDataArray()), + "BlockLight" => new ByteArray("BlockLight", $section->getLightArray()), + "SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray()) + ]); + } + + $nbt->Biomes = new ByteArray("Biomes", $this->getBiomeIdArray()); + $nbt->BiomeColors = new IntArray("BiomeColors", $this->getBiomeColorArray()); + + $entities = []; + + foreach($this->getEntities() as $entity){ + if(!($entity instanceof Player) and $entity->closed !== true){ + $entity->saveNBT(); + $entities[] = $entity->namedtag; + } + } + + $nbt->Entities = new Enum("Entities", $entities); + $nbt->Entities->setTagType(NBT::TAG_Compound); + + + $tiles = []; + foreach($this->getTiles() as $tile){ + if($tile->closed !== true){ + $tile->saveNBT(); + $tiles[] = $tile->namedtag; + } + } + + $nbt->Entities = new Enum("TileEntities", $tiles); + $nbt->Entities->setTagType(NBT::TAG_Compound); + $writer = new NBT(NBT::BIG_ENDIAN); + $nbt->setName("Level"); + $writer->setData(new Compound("", array("Level" => $nbt))); + return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); + } } \ No newline at end of file diff --git a/src/pocketmine/level/format/anvil/ChunkRequestTask.php b/src/pocketmine/level/format/anvil/ChunkRequestTask.php index d3d082d5c..22661ecce 100644 --- a/src/pocketmine/level/format/anvil/ChunkRequestTask.php +++ b/src/pocketmine/level/format/anvil/ChunkRequestTask.php @@ -48,7 +48,10 @@ class ChunkRequestTask extends AsyncTask{ $this->levelId = $levelId; $this->chunkX = $chunkX; $this->chunkZ = $chunkZ; - $chunk = $level->getChunk($chunkX, $chunkZ, true); + $chunk = $level->getChunk($chunkX, $chunkZ, false); + if(!($chunk instanceof Chunk)){ + throw new \Exception("Invalid Chunk sent"); + } $this->biomeIds = $chunk->getBiomeIdArray(); $this->biomeColors = $chunk->getBiomeColorArray(); diff --git a/src/pocketmine/level/format/anvil/ChunkSnapshot.php b/src/pocketmine/level/format/anvil/ChunkSnapshot.php deleted file mode 100644 index d9e25fc9c..000000000 --- a/src/pocketmine/level/format/anvil/ChunkSnapshot.php +++ /dev/null @@ -1,78 +0,0 @@ -blockId{ - (($y >> 4) << 12) //get section index - + ($y << 8) + ($z << 4) + $x //get block index in section - }); - } - - public function getBlockData($x, $y, $z){ - $data = ord($this->blockData{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $data & 0x0F; - }else{ - return $data >> 4; - } - } - - public function getBlockSkyLight($x, $y, $z){ - $level = ord($this->skyLight{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $level & 0x0F; - }else{ - return $level >> 4; - } - } - - public function getBlockLight($x, $y, $z){ - $level = ord($this->light{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $level & 0x0F; - }else{ - return $level >> 4; - } - } - - public function getBiome(){ - return 0; //TODO - } - - public function getHighestBlockAt($x, $z){ - return 127; //TODO - } -} \ 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 b465e1be0..c8a2885c2 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\mcregion\Chunk; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\Byte; use pocketmine\nbt\tag\ByteArray; @@ -97,16 +98,9 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ return false; } - $nbt = new NBT(NBT::BIG_ENDIAN); - $nbt->readCompressed(fread($this->filePointer, $length - 1), $compression); - $chunk = $nbt->getData(); + $chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider); - - if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){ - return false; - } - - return new Chunk($this->levelProvider, $chunk->Level); + return $chunk instanceof Chunk ? $chunk : false; } public function generateChunk($x, $z){ @@ -129,51 +123,15 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ $nbt->TileEntities->setTagType(NBT::TAG_Compound); $nbt->TileTicks = new Enum("TileTicks", []); $nbt->TileTicks->setTagType(NBT::TAG_Compound); - $this->saveChunk($x, $z, $nbt); + $writer = new NBT(NBT::BIG_ENDIAN); + $nbt->setName("Level"); + $writer->setData(new Compound("", array("Level" => $nbt))); + $chunkData= $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); + $this->saveChunk($x, $z, $chunkData); } public function writeChunk(Chunk $chunk){ - $nbt = $chunk->getNBT(); - $nbt->Sections = new Enum("Sections", []); - $nbt->Sections->setTagType(NBT::TAG_Compound); - foreach($chunk->getSections() as $section){ - $nbt->Sections[$section->getY()] = new Compound(null, [ - "Y" => new Byte("Y", $section->getY()), - "Blocks" => new ByteArray("Blocks", $section->getIdArray()), - "Data" => new ByteArray("Data", $section->getDataArray()), - "BlockLight" => new ByteArray("BlockLight", $section->getLightArray()), - "SkyLight" => new ByteArray("SkyLight", $section->getSkyLightArray()) - ]); - } - - $nbt->Biomes = new ByteArray("Biomes", $chunk->getBiomeIdArray()); - $nbt->BiomeColors = new IntArray("BiomeColors", $chunk->getBiomeColorArray()); - - $entities = []; - - foreach($chunk->getEntities() as $entity){ - if(!($entity instanceof Player) and $entity->closed !== true){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new Enum("Entities", $entities); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - - $tiles = []; - foreach($chunk->getTiles() as $tile){ - if($tile->closed !== true){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - } - - $nbt->Entities = new Enum("TileEntities", $tiles); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt); + $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunk->toBinary()); } } \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/BaseChunk.php b/src/pocketmine/level/format/generic/BaseChunk.php index 5348f40ed..50e43c87e 100644 --- a/src/pocketmine/level/format/generic/BaseChunk.php +++ b/src/pocketmine/level/format/generic/BaseChunk.php @@ -70,8 +70,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ * * @throws \Exception */ - protected function __construct(LevelProvider $level, $x, $z, array $sections, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){ - $this->level = new \WeakRef($level); + protected function __construct($level, $x, $z, array $sections, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){ + $this->level = $level instanceof LevelProvider ? new \WeakRef($level) : $level; $this->x = (int) $x; $this->z = (int) $z; foreach($sections as $Y => $section){ @@ -99,46 +99,48 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ $this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")); } - $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); - foreach($entities as $nbt){ - if($nbt instanceof Compound){ - if(!isset($nbt->id)){ - continue; - } + if($this->getLevel() instanceof LevelProvider){ + $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); + foreach($entities as $nbt){ + if($nbt instanceof Compound){ + if(!isset($nbt->id)){ + continue; + } - if($nbt->id instanceof String){ //New format + if($nbt->id instanceof String){ //New format + switch($nbt["id"]){ + case "Item": + (new DroppedItem($this, $nbt))->spawnToAll(); + break; + } + }else{ //Old format + + } + } + } + $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); + + $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); + foreach($tiles as $nbt){ + if($nbt instanceof Compound){ + if(!isset($nbt->id)){ + continue; + } switch($nbt["id"]){ - case "Item": - (new DroppedItem($this, $nbt))->spawnToAll(); + case Tile::CHEST: + new Chest($this, $nbt); + break; + case Tile::FURNACE: + new Furnace($this, $nbt); + break; + case Tile::SIGN: + new Sign($this, $nbt); break; } - }else{ //Old format - } } + $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); } - $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); - - $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); - foreach($tiles as $nbt){ - if($nbt instanceof Compound){ - if(!isset($nbt->id)){ - continue; - } - switch($nbt["id"]){ - case Tile::CHEST: - new Chest($this, $nbt); - break; - case Tile::FURNACE: - new Furnace($this, $nbt); - break; - case Tile::SIGN: - new Sign($this, $nbt); - break; - } - } - } - $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); } public function getBlock($x, $y, $z, &$blockId, &$meta = null){ @@ -149,7 +151,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ try{ return $this->sections[$y >> 4]->setBlock($x, $y & 0x0f, $z, $blockId & 0xff, $meta & 0x0f); }catch(\Exception $e){ - $this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y)); + $level = $this->getLevel(); + $this->setSection($Y = $y >> 4, $level::createChunkSection($Y)); return $this->setBlock($x, $y, $z, $blockId, $meta); } } @@ -162,7 +165,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ try{ $this->sections[$y >> 4]->setBlockId($x, $y & 0x0f, $z, $id); }catch(\Exception $e){ - $this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y)); + $level = $this->getLevel(); + $this->setSection($Y = $y >> 4, $level::createChunkSection($Y)); $this->setBlockId($x, $y, $z, $id); } } @@ -175,7 +179,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ try{ $this->sections[$y >> 4]->setBlockData($x, $y & 0x0f, $z, $data); }catch(\Exception $e){ - $this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y)); + $level = $this->getLevel(); + $this->setSection($Y = $y >> 4, $level::createChunkSection($Y)); $this->setBlockData($x, $y, $z, $data); } } @@ -188,7 +193,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ try{ $this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z, $data); }catch(\Exception $e){ - $this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y)); + $level = $this->getLevel(); + $this->setSection($Y = $y >> 4, $level::createChunkSection($Y)); $this->setBlockSkyLight($x, $y, $z, $data); } } @@ -201,7 +207,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{ try{ $this->sections[$y >> 4]->getBlockSkyLight($x, $y & 0x0f, $z, $data); }catch(\Exception $e){ - $this->setSection($Y = $y >> 4, $this->getLevel()->createChunkSection($Y)); + $level = $this->getLevel(); + $this->setSection($Y = $y >> 4, $level::createChunkSection($Y)); $this->setBlockLight($x, $y, $z, $data); } } diff --git a/src/pocketmine/level/format/generic/BaseChunkSnapshot.php b/src/pocketmine/level/format/generic/BaseChunkSnapshot.php deleted file mode 100644 index 4d08b4f2d..000000000 --- a/src/pocketmine/level/format/generic/BaseChunkSnapshot.php +++ /dev/null @@ -1,64 +0,0 @@ -x = $x; - $this->z = $z; - $this->levelName = $levelName; - $this->levelTime = $levelTime; - $this->blockId = $blockId; - $this->blockData = $blockData; - $this->skyLight = $skyLight; - $this->light = $light; - } - - public function getX(){ - return $this->x; - } - - public function getZ(){ - return $this->z; - } - - public function getLevelName(){ - return $this->levelName; - } - - public function getLevelTime(){ - return $this->levelTime; - } -} \ No newline at end of file diff --git a/src/pocketmine/level/format/generic/BaseFullChunk.php b/src/pocketmine/level/format/generic/BaseFullChunk.php index 6d4ae583b..232abae55 100644 --- a/src/pocketmine/level/format/generic/BaseFullChunk.php +++ b/src/pocketmine/level/format/generic/BaseFullChunk.php @@ -76,8 +76,8 @@ abstract class BaseFullChunk implements FullChunk{ * * @throws \Exception */ - protected function __construct(LevelProvider $level, $x, $z, $blocks, $data, $skyLight, $blockLight, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){ - $this->level = new \WeakRef($level); + protected function __construct($level, $x, $z, $blocks, $data, $skyLight, $blockLight, $biomeIds = null, array $biomeColors = [], array $entities = [], array $tiles = []){ + $this->level = $level instanceof LevelProvider ? new \WeakRef($level) : $level; $this->x = (int) $x; $this->z = (int) $z; @@ -98,46 +98,48 @@ abstract class BaseFullChunk implements FullChunk{ $this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a")); } - $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); - foreach($entities as $nbt){ - if($nbt instanceof Compound){ - if(!isset($nbt->id)){ - continue; - } + if($this->getLevel() instanceof LevelProvider){ + $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming(); + foreach($entities as $nbt){ + if($nbt instanceof Compound){ + if(!isset($nbt->id)){ + continue; + } - if($nbt->id instanceof String){ //New format + if($nbt->id instanceof String){ //New format + switch($nbt["id"]){ + case "Item": + (new DroppedItem($this, $nbt))->spawnToAll(); + break; + } + }else{ //Old format + + } + } + } + $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); + + $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); + foreach($tiles as $nbt){ + if($nbt instanceof Compound){ + if(!isset($nbt->id)){ + continue; + } switch($nbt["id"]){ - case "Item": - (new DroppedItem($this, $nbt))->spawnToAll(); + case Tile::CHEST: + new Chest($this, $nbt); + break; + case Tile::FURNACE: + new Furnace($this, $nbt); + break; + case Tile::SIGN: + new Sign($this, $nbt); break; } - }else{ //Old format - } } + $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); } - $this->getLevel()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming(); - - $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming(); - foreach($tiles as $nbt){ - if($nbt instanceof Compound){ - if(!isset($nbt->id)){ - continue; - } - switch($nbt["id"]){ - case Tile::CHEST: - new Chest($this, $nbt); - break; - case Tile::FURNACE: - new Furnace($this, $nbt); - break; - case Tile::SIGN: - new Sign($this, $nbt); - break; - } - } - } - $this->getLevel()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming(); } public function getX(){ @@ -148,11 +150,19 @@ abstract class BaseFullChunk implements FullChunk{ return $this->z; } + public function setX($x){ + $this->x = $x; + } + + public function setZ($z){ + $this->z = $z; + } + /** * @return LevelProvider */ public function getLevel(){ - return $this->level->valid() ? $this->level->get() : null; + return $this->level instanceof \WeakRef ? ($this->level->valid() ? $this->level->get() : null) : $this->level; } public function getBiomeId($x, $z){ diff --git a/src/pocketmine/level/format/mcregion/Chunk.php b/src/pocketmine/level/format/mcregion/Chunk.php index 52fe2ddff..d5ba9054d 100644 --- a/src/pocketmine/level/format/mcregion/Chunk.php +++ b/src/pocketmine/level/format/mcregion/Chunk.php @@ -29,6 +29,7 @@ use pocketmine\nbt\tag\ByteArray; use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\IntArray; +use pocketmine\Player; use pocketmine\utils\Binary; class Chunk extends BaseFullChunk{ @@ -36,7 +37,7 @@ class Chunk extends BaseFullChunk{ /** @var Compound */ protected $nbt; - public function __construct(LevelProvider $level, Compound $nbt){ + public function __construct($level, Compound $nbt){ $this->nbt = $nbt; if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){ @@ -209,13 +210,76 @@ class Chunk extends BaseFullChunk{ $this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value); } - public function getChunkSnapshot($includeMaxBlockY = true, $includeBiome = false, $includeBiomeTemp = false){ - $emptySections = [false, false, false, false, false, false, false, false]; + /** + * @return bool + */ + public function isGenerated(){ + return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0); + } - //TODO: maxBlockY, biomeMap, biomeTemp + /** + * @param int $value + */ + public function setGenerated($value = 1){ + $this->nbt->TerrainGenerated = new Byte("TerrainGenerated", $value); + } - //TODO: time - return new ChunkSnapshot($this->getX(), $this->getZ(), $this->getLevel()->getName(), 0 /*$this->getLevel()->getTime()*/, $this->getBlockIdArray(), $this->getBlockDataArray(), $this->getBlockSkyLightArray(), $this->getBlockLightArray(), $emptySections, null, null, null, null); + /** + * @param string $data + * @param LevelProvider $provider + * + * @return Chunk + */ + public static function fromBinary($data, LevelProvider $provider = null){ + $nbt = new NBT(NBT::BIG_ENDIAN); + $nbt->readCompressed($data, ZLIB_ENCODING_DEFLATE); + $chunk = $nbt->getData(); + + if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){ + return null; + } + + return new Chunk($provider instanceof LevelProvider ? $provider : "pocketmine\\level\\format\\mcregion\\McRegion", $chunk->Level); + } + + public function toBinary(){ + $nbt = $this->getNBT(); + + $nbt->Blocks = new ByteArray("Blocks", $this->getBlockIdArray()); + $nbt->Data = new ByteArray("Data", $this->getBlockDataArray()); + $nbt->SkyLight = new ByteArray("SkyLight", $this->getBlockSkyLightArray()); + $nbt->BlockLight = new ByteArray("BlockLight", $this->getBlockLightArray()); + + $nbt->Biomes = new ByteArray("Biomes", $this->getBiomeIdArray()); + $nbt->BiomeColors = new IntArray("BiomeColors", $this->getBiomeColorArray()); + + $entities = []; + + foreach($this->getEntities() as $entity){ + if(!($entity instanceof Player) and $entity->closed !== true){ + $entity->saveNBT(); + $entities[] = $entity->namedtag; + } + } + + $nbt->Entities = new Enum("Entities", $entities); + $nbt->Entities->setTagType(NBT::TAG_Compound); + + + $tiles = []; + foreach($this->getTiles() as $tile){ + if($tile->closed !== true){ + $tile->saveNBT(); + $tiles[] = $tile->namedtag; + } + } + + $nbt->Entities = new Enum("TileEntities", $tiles); + $nbt->Entities->setTagType(NBT::TAG_Compound); + $writer = new NBT(NBT::BIG_ENDIAN); + $nbt->setName("Level"); + $writer->setData(new Compound("", array("Level" => $nbt))); + return $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); } /** diff --git a/src/pocketmine/level/format/mcregion/ChunkRequestTask.php b/src/pocketmine/level/format/mcregion/ChunkRequestTask.php index ad0e7f941..badd3c419 100644 --- a/src/pocketmine/level/format/mcregion/ChunkRequestTask.php +++ b/src/pocketmine/level/format/mcregion/ChunkRequestTask.php @@ -51,7 +51,10 @@ class ChunkRequestTask extends AsyncTask{ $this->levelId = $levelId; $this->chunkX = $chunkX; $this->chunkZ = $chunkZ; - $chunk = $level->getChunk($chunkX, $chunkZ, true); + $chunk = $level->getChunk($chunkX, $chunkZ, false); + if(!($chunk instanceof Chunk)){ + throw new \Exception("Invalid Chunk sent"); + } $this->blocks = $chunk->getBlockIdArray(); $this->data = $chunk->getBlockDataArray(); $this->skyLight = $chunk->getBlockSkyLightArray(); diff --git a/src/pocketmine/level/format/mcregion/ChunkSnapshot.php b/src/pocketmine/level/format/mcregion/ChunkSnapshot.php deleted file mode 100644 index ce0e9f91e..000000000 --- a/src/pocketmine/level/format/mcregion/ChunkSnapshot.php +++ /dev/null @@ -1,78 +0,0 @@ -blockId{ - (($y >> 4) << 12) //get section index - + ($y << 8) + ($z << 4) + $x //get block index in section - }); - } - - public function getBlockData($x, $y, $z){ - $data = ord($this->blockData{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $data & 0x0F; - }else{ - return $data >> 4; - } - } - - public function getBlockSkyLight($x, $y, $z){ - $level = ord($this->skyLight{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $level & 0x0F; - }else{ - return $level >> 4; - } - } - - public function getBlockLight($x, $y, $z){ - $level = ord($this->light{ - (($y >> 4) << 11) //get section index - + ($y << 7) + ($z << 3) + ($x >> 1) //get block index in section - }); - if(($y & 1) === 0){ - return $level & 0x0F; - }else{ - return $level >> 4; - } - } - - public function getBiome(){ - return 0; //TODO - } - - public function getHighestBlockAt($x, $z){ - return 127; //TODO - } -} \ 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 2088a04a6..1c4b0c13d 100644 --- a/src/pocketmine/level/format/mcregion/McRegion.php +++ b/src/pocketmine/level/format/mcregion/McRegion.php @@ -21,8 +21,8 @@ namespace pocketmine\level\format\mcregion; +use pocketmine\level\format\FullChunk; use pocketmine\level\format\generic\BaseLevelProvider; -use pocketmine\level\format\SimpleChunk; use pocketmine\level\generator\Generator; use pocketmine\level\Level; use pocketmine\nbt\NBT; @@ -219,12 +219,12 @@ class McRegion extends BaseLevelProvider{ } } - public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){ - return; - //TODO! + public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ + if(!($chunk instanceof Chunk)){ + throw new \Exception("Invalid Chunk class"); + } - - if($chunk->isGenerated() === false){ + if($chunk->isPopulated() === false){ $this->unloadChunk($chunkX, $chunkZ, false); $regionX = $regionZ = null; self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); @@ -233,38 +233,21 @@ class McRegion extends BaseLevelProvider{ $region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32); $this->loadChunk($chunkX, $chunkZ); }else{ - $newChunk = $this->getChunk($chunkX, $chunkZ, true); - for($y = 0; $y < 8; ++$y){ - /*$section = new ChunkSection(new Compound(null, [ - "Y" => new Byte("Y", $y), - "Blocks" => new ByteArray("Blocks", $chunk->getSectionIds($y)), - "Data" => new ByteArray("Data", $chunk->getSectionData($y)), - "SkyLight" => new ByteArray("SkyLight", str_repeat("\xff", 2048)), //TODO - "BlockLight" => new ByteArray("BlockLight", str_repeat("\x00", 2048)) //TODO - ])); - $newChunk->setSection($y, $section);*/ - } - if($chunk->isPopulated()){ - $newChunk->setPopulated(1); - } + $newChunk = clone $chunk; + $newChunk->setX($chunkX); + $newChunk->setZ($chunkZ); $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $newChunk; - $this->saveChunk($chunkX, $chunkZ); + //$this->saveChunk($chunkX, $chunkZ); } } - public function createChunkSection($Y){ - return new ChunkSection( - $Y, - str_repeat("\xff", 4096), - $half = str_repeat("\xff", 2048), - $half, - $half - ); + public static function createChunkSection($Y){ + return null; } public function isChunkGenerated($chunkX, $chunkZ){ if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) instanceof RegionLoader){ - return $region->chunkExists($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32); + 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; diff --git a/src/pocketmine/level/format/mcregion/RegionLoader.php b/src/pocketmine/level/format/mcregion/RegionLoader.php index 4021574ba..a15299abb 100644 --- a/src/pocketmine/level/format/mcregion/RegionLoader.php +++ b/src/pocketmine/level/format/mcregion/RegionLoader.php @@ -119,16 +119,9 @@ class RegionLoader{ return false; } - $nbt = new NBT(NBT::BIG_ENDIAN); - $nbt->readCompressed(fread($this->filePointer, $length - 1), $compression); - $chunk = $nbt->getData(); + $chunk = Chunk::fromBinary(fread($this->filePointer, $length - 1), $this->levelProvider); - - if(!isset($chunk->Level) or !($chunk->Level instanceof Compound)){ - return false; - } - - return new Chunk($this->levelProvider, $chunk->Level); + return $chunk instanceof Chunk ? $chunk : false; } public function chunkExists($x, $z){ @@ -159,14 +152,15 @@ class RegionLoader{ $nbt->TileEntities->setTagType(NBT::TAG_Compound); $nbt->TileTicks = new Enum("TileTicks", []); $nbt->TileTicks->setTagType(NBT::TAG_Compound); - $this->saveChunk($x, $z, $nbt); - } - - protected function saveChunk($x, $z, Compound $nbt){ $writer = new NBT(NBT::BIG_ENDIAN); $nbt->setName("Level"); $writer->setData(new Compound("", array("Level" => $nbt))); $chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL); + + $this->saveChunk($x, $z, $chunkData); + } + + protected function saveChunk($x, $z, $chunkData){ $length = strlen($chunkData) + 1; $sectors = (int) ceil(($length + 4) / 4096); $index = self::getChunkOffset($x, $z); @@ -187,41 +181,7 @@ class RegionLoader{ } public function writeChunk(Chunk $chunk){ - $nbt = $chunk->getNBT(); - - $nbt->Blocks = new ByteArray("Blocks", $chunk->getBlockIdArray()); - $nbt->Data = new ByteArray("Data", $chunk->getBlockDataArray()); - $nbt->SkyLight = new ByteArray("SkyLight", $chunk->getBlockSkyLightArray()); - $nbt->BlockLight = new ByteArray("BlockLight", $chunk->getBlockLightArray()); - - $nbt->Biomes = new ByteArray("Biomes", $chunk->getBiomeIdArray()); - $nbt->BiomeColors = new IntArray("BiomeColors", $chunk->getBiomeColorArray()); - - $entities = []; - - foreach($chunk->getEntities() as $entity){ - if(!($entity instanceof Player) and $entity->closed !== true){ - $entity->saveNBT(); - $entities[] = $entity->namedtag; - } - } - - $nbt->Entities = new Enum("Entities", $entities); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - - $tiles = []; - foreach($chunk->getTiles() as $tile){ - if($tile->closed !== true){ - $tile->saveNBT(); - $tiles[] = $tile->namedtag; - } - } - - $nbt->Entities = new Enum("TileEntities", $tiles); - $nbt->Entities->setTagType(NBT::TAG_Compound); - - $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $nbt); + $this->saveChunk($chunk->getX() - ($this->getX() * 32), $chunk->getZ() - ($this->getZ() * 32), $chunk->toBinary()); } protected static function getChunkOffset($x, $z){ diff --git a/src/pocketmine/level/generator/Flat.php b/src/pocketmine/level/generator/Flat.php index 5cab14e55..9c03a9dda 100644 --- a/src/pocketmine/level/generator/Flat.php +++ b/src/pocketmine/level/generator/Flat.php @@ -21,6 +21,7 @@ namespace pocketmine\level\generator; +use pocketmine\level\format\FullChunk; use pocketmine\block\CoalOre; use pocketmine\block\DiamondOre; use pocketmine\block\Dirt; @@ -30,7 +31,6 @@ use pocketmine\block\IronOre; use pocketmine\block\LapisOre; use pocketmine\block\RedstoneOre; use pocketmine\item\Item; -use pocketmine\level\format\SimpleChunk; use pocketmine\level\generator\populator\Ore; use pocketmine\level\generator\populator\Populator; use pocketmine\math\Vector3 as Vector3; @@ -39,7 +39,7 @@ use pocketmine\utils\Random; class Flat extends Generator{ /** @var GenerationChunkManager */ private $level; - /** @var SimpleChunk */ + /** @var FullChunk */ private $chunk; /** @var Random */ private $random; @@ -59,11 +59,7 @@ class Flat extends Generator{ $this->preset = "2;7,2x3,2;1;"; //$this->preset = "2;7,59x1,3x3,2;1;spawn(radius=10 block=89),decoration(treecount=80 grasscount=45)"; $this->options = $options; - if(isset($options["preset"]) and $options["preset"] != ""){ - $this->parsePreset($options["preset"]); - }else{ - $this->parsePreset($this->preset); - } + if(isset($this->options["decoration"])){ $ores = new Ore(); $ores->setOreTypes(array( @@ -110,7 +106,8 @@ class Flat extends Generator{ } - $this->chunk = new SimpleChunk(null, null, SimpleChunk::FLAG_GENERATED); + $this->chunk = $this->level->getChunk(0, 0); + $this->chunk->setGenerated(); for($Z = 0; $Z < 16; ++$Z){ for($X = 0; $X < 16; ++$X){ @@ -145,6 +142,13 @@ class Flat extends Generator{ public function init(GenerationChunkManager $level, Random $random){ $this->level = $level; $this->random = $random; + + if(isset($this->options["preset"]) and $this->options["preset"] != ""){ + $this->parsePreset($this->options["preset"]); + }else{ + $this->parsePreset($this->preset); + } + } public function generateChunk($chunkX, $chunkZ){ diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php index 757afc8df..eba2e89cf 100644 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -22,7 +22,7 @@ namespace pocketmine\level\generator; use pocketmine\level\ChunkManager; -use pocketmine\level\format\SimpleChunk; +use pocketmine\level\format\FullChunk; use pocketmine\level\Level; use pocketmine\utils\Random; @@ -30,10 +30,10 @@ class GenerationChunkManager implements ChunkManager{ protected $levelID; - /** @var SimpleChunk[] */ + /** @var FullChunk[] */ protected $chunks = []; - /** @var \SplObjectStorage */ + /** @var \SplObjectStorage */ protected $unloadQueue; /** @var Generator */ @@ -44,6 +44,8 @@ class GenerationChunkManager implements ChunkManager{ protected $seed; + protected $changes = []; + public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){ if(!is_subclass_of($class, "pocketmine\\level\\generator\\Generator")){ throw new \Exception("Class is not a subclass of Generator"); @@ -77,7 +79,7 @@ class GenerationChunkManager implements ChunkManager{ * @param $chunkX * @param $chunkZ * - * @return SimpleChunk + * @return FullChunk */ public function getChunk($chunkX, $chunkZ){ $index = Level::chunkHash($chunkX, $chunkZ); @@ -87,28 +89,24 @@ class GenerationChunkManager implements ChunkManager{ } /** - * @param bool $set - * - * @return SimpleChunk[] + * @return FullChunk[] */ - public function getChangedChunks($set = true){ - $changed = []; - foreach($this->chunks as $chunk){ - if($chunk->hasChanged($set)){ - $changed[] = $chunk; - } - } + public function getChangedChunks(){ + return $this->changes; + } - return $changed; + public function cleanChangedChunks(){ + $this->changes = []; } public function doGarbageCollection(){ if($this->unloadQueue->count() > 0){ - /** @var SimpleChunk $chunk */ + /** @var FullChunk $chunk */ foreach($this->unloadQueue as $chunk){ - if(!$chunk->hasChanged(false)){ - unset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]); + if(isset($this->changes[$index = Level::chunkHash($chunk->getX(), $chunk->getZ())])){ + continue; } + unset($this->chunks[$index]); $this->unloadQueue->detach($chunk); } } @@ -119,7 +117,7 @@ class GenerationChunkManager implements ChunkManager{ } public function generateChunk($chunkX, $chunkZ){ - $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = new SimpleChunk($chunkX, $chunkZ, 0); + $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $this->requestChunk($chunkX, $chunkZ); $this->generator->generateChunk($chunkX, $chunkZ); $this->setChunkGenerated($chunkX, $chunkZ); } @@ -150,11 +148,15 @@ class GenerationChunkManager implements ChunkManager{ } public function setChunkGenerated($chunkX, $chunkZ){ - $this->getChunk($chunkX, $chunkZ)->setGenerated(true); + $chunk = $this->getChunk($chunkX, $chunkZ); + $chunk->setGenerated(true); + $this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk; } public function setChunkPopulated($chunkX, $chunkZ){ - $this->getChunk($chunkX, $chunkZ)->setPopulated(true); + $chunk = $this->getChunk($chunkX, $chunkZ); + $chunk->setPopulated(true); + $this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk; } protected function requestChunk($chunkX, $chunkZ){ @@ -166,11 +168,12 @@ class GenerationChunkManager implements ChunkManager{ /** * @param int $chunkX * @param int $chunkZ - * @param SimpleChunk $chunk + * @param FullChunk $chunk */ - public function setChunk($chunkX, $chunkZ, SimpleChunk $chunk){ - $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; - if($chunk->isGenerated() and $chunk->isPopulated()){ + public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ + $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; + $this->changes[$index] = $chunk; + if($chunk->isPopulated()){ //TODO: Queue to be sent } } diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php index 98e58739d..d945ad988 100644 --- a/src/pocketmine/level/generator/GenerationManager.php +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -21,7 +21,7 @@ namespace pocketmine\level\generator; -use pocketmine\level\format\SimpleChunk; +use pocketmine\level\format\FullChunk; use pocketmine\level\Level; use pocketmine\utils\Binary; @@ -60,7 +60,8 @@ class GenerationManager{ * int32 levelID * int32 chunkX * int32 chunkZ - * byte flags (1 generated, 2 populated) + * byte className length + * byte[] className * byte[] chunk (none if generated flag is not set) */ const PACKET_SEND_CHUNK = 0x02; @@ -144,10 +145,11 @@ class GenerationManager{ if(isset($this->levels[$levelID])){ $this->generatedQueue[$levelID][$index] = true; if(count($this->generatedQueue[$levelID]) > 6){ - $this->levels[$levelID]->doGarbageCollection(); - foreach($this->levels[$levelID]->getChangedChunks(true) as $chunk){ + foreach($this->levels[$levelID]->getChangedChunks() as $chunk){ $this->sendChunk($levelID, $chunk); } + $this->levels[$levelID]->doGarbageCollection(); + $this->levels[$levelID]->cleanChangedChunks(); $this->generatedQueue[$levelID] = []; } } @@ -166,7 +168,7 @@ class GenerationManager{ $this->requestQueue->enqueue([$levelID, $chunkX, $chunkZ]); } - protected function receiveChunk($levelID, SimpleChunk $chunk){ + protected function receiveChunk($levelID, FullChunk $chunk){ if($this->needsChunk !== null and $this->needsChunk[0] === $levelID){ if($this->needsChunk[1] === $chunk->getX() and $this->needsChunk[2] === $chunk->getZ()){ $this->needsChunk = $chunk; @@ -180,7 +182,7 @@ class GenerationManager{ * @param $chunkX * @param $chunkZ * - * @return SimpleChunk + * @return FullChunk */ public function requestChunk($levelID, $chunkX, $chunkZ){ $this->needsChunk = [$levelID, $chunkX, $chunkZ]; @@ -188,19 +190,19 @@ class GenerationManager{ @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); do{ $this->readPacket(); - }while($this->shutdown !== true and !($this->needsChunk instanceof SimpleChunk)); + }while($this->shutdown !== true and !($this->needsChunk instanceof FullChunk)); $chunk = $this->needsChunk; $this->needsChunk = null; - if($chunk instanceof SimpleChunk){ + if($chunk instanceof FullChunk){ return $chunk; }else{ - return new SimpleChunk($chunkX, $chunkZ, 0); + return null; } } - public function sendChunk($levelID, SimpleChunk $chunk){ - $binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . $chunk->toBinary(); + public function sendChunk($levelID, FullChunk $chunk){ + $binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); } @@ -234,7 +236,11 @@ class GenerationManager{ }elseif($pid === self::PACKET_SEND_CHUNK){ $levelID = Binary::readInt(substr($packet, $offset, 4)); $offset += 4; - $chunk = SimpleChunk::fromBinary(substr($packet, $offset)); + $len = ord($packet{$offset++}); + /** @var FullChunk $class */ + $class = substr($packet, $offset, $len); + $offset += $len; + $chunk = $class::fromBinary(substr($packet, $offset)); $this->receiveChunk($levelID, $chunk); }elseif($pid === self::PACKET_OPEN_LEVEL){ $levelID = Binary::readInt(substr($packet, $offset, 4)); diff --git a/src/pocketmine/level/generator/GenerationRequestManager.php b/src/pocketmine/level/generator/GenerationRequestManager.php index 378f5b0b9..f8029bbe3 100644 --- a/src/pocketmine/level/generator/GenerationRequestManager.php +++ b/src/pocketmine/level/generator/GenerationRequestManager.php @@ -21,7 +21,7 @@ namespace pocketmine\level\generator; -use pocketmine\level\format\SimpleChunk; +use pocketmine\level\format\FullChunk; use pocketmine\level\Level; use pocketmine\Server; use pocketmine\utils\Binary; @@ -77,8 +77,8 @@ class GenerationRequestManager{ return $buffer; } - protected function sendChunk($levelID, SimpleChunk $chunk){ - $binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . $chunk->toBinary(); + protected function sendChunk($levelID, FullChunk $chunk){ + $binary = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); } @@ -89,14 +89,14 @@ class GenerationRequestManager{ protected function handleRequest($levelID, $chunkX, $chunkZ){ if(($level = $this->server->getLevel($levelID)) instanceof Level){ - $this->sendChunk($levelID, $level->getChunk($chunkX, $chunkZ, false)); + $this->sendChunk($levelID, $level->getChunkAt($chunkX, $chunkZ, true)); }else{ $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID); @socket_write($this->socket, Binary::writeInt(strlen($buffer)) . $buffer); } } - protected function receiveChunk($levelID, SimpleChunk $chunk){ + protected function receiveChunk($levelID, FullChunk $chunk){ if(($level = $this->server->getLevel($levelID)) instanceof Level){ $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); }else{ @@ -106,10 +106,11 @@ class GenerationRequestManager{ } public function handlePackets(){ - if(($len = @socket_read($this->socket, 4)) !== false){ + if(($len = @socket_read($this->socket, 4)) !== false and $len !== ""){ if(strlen($len) < 4){ $len .= $this->socketRead(4 - strlen($len)); } + $packet = $this->socketRead(Binary::readInt($len)); $pid = ord($packet{0}); $offset = 1; @@ -124,7 +125,11 @@ class GenerationRequestManager{ }elseif($pid === GenerationManager::PACKET_SEND_CHUNK){ $levelID = Binary::readInt(substr($packet, $offset, 4)); $offset += 4; - $chunk = SimpleChunk::fromBinary(substr($packet, $offset)); + $len = ord($packet{$offset++}); + /** @var FullChunk $class */ + $class = substr($packet, $offset, $len); + $offset += $len; + $chunk = $class::fromBinary(substr($packet, $offset)); $this->receiveChunk($levelID, $chunk); } } diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php index d9ae29816..1c4dca32f 100644 --- a/src/pocketmine/level/generator/GenerationThread.php +++ b/src/pocketmine/level/generator/GenerationThread.php @@ -87,6 +87,7 @@ class GenerationThread extends \Thread{ } public function run(){ + error_reporting(-1); //Load removed dependencies, can't use require_once() foreach($this->loadPaths as $name => $path){ if(!class_exists($name, false) and !class_exists($name, false)){