From db409851e98abd98aa157ce6aafdb3de7254ea2c Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Date: Sat, 9 May 2015 19:03:41 +0200 Subject: [PATCH] World generation is timed by type and chunks can be created on the fly --- src/pocketmine/Player.php | 6 +- src/pocketmine/Server.php | 2 - src/pocketmine/event/Timings.php | 9 +- src/pocketmine/level/Level.php | 33 ++--- src/pocketmine/level/format/FullChunk.php | 9 ++ src/pocketmine/level/format/anvil/Anvil.php | 4 + src/pocketmine/level/format/anvil/Chunk.php | 38 +++++- .../level/format/anvil/RegionLoader.php | 29 ----- src/pocketmine/level/format/leveldb/Chunk.php | 15 +++ .../level/format/leveldb/LevelDB.php | 12 +- .../level/format/mcregion/Chunk.php | 39 +++++- .../level/format/mcregion/McRegion.php | 11 +- .../level/format/mcregion/RegionLoader.php | 62 +-------- .../level/generator/PopulationTask.php | 120 ++++++++++-------- 14 files changed, 212 insertions(+), 177 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 7c6054d72..50d232e50 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -664,7 +664,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ ++$count; if(!$this->level->populateChunk($X, $Z)){ - if($this->teleportPosition === null){ + if($this->spawned and $this->teleportPosition === null){ continue; }else{ break; @@ -1391,10 +1391,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->timings->startTiming(); if($this->spawned){ - $this->processMovement($tickDiff); - $this->entityBaseTick($tickDiff); if(!$this->isSpectator()){ @@ -1575,8 +1573,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ if($p->kick("logged in from another location") === false){ $this->close($this->getLeaveMessage(), "Logged in from another location"); - return; - }else{ return; } } diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 97f01bd55..52af8e3ad 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1228,8 +1228,6 @@ class Server{ asort($order); - $chunkX = $chunkZ = null; - foreach($order as $index => $distance){ Level::getXZ($index, $chunkX, $chunkZ); $level->generateChunk($chunkX, $chunkZ, true); diff --git a/src/pocketmine/event/Timings.php b/src/pocketmine/event/Timings.php index f6133adc2..43dda08c8 100644 --- a/src/pocketmine/event/Timings.php +++ b/src/pocketmine/event/Timings.php @@ -55,6 +55,10 @@ abstract class Timings{ /** @var TimingsHandler */ public static $generationTimer; /** @var TimingsHandler */ + public static $populationTimer; + /** @var TimingsHandler */ + public static $generationCallbackTimer; + /** @var TimingsHandler */ public static $permissibleCalculationTimer; /** @var TimingsHandler */ public static $permissionDefaultTimer; @@ -81,8 +85,6 @@ abstract class Timings{ /** @var TimingsHandler */ public static $timerEntityTickRest; - /** @var TimingsHandler */ - public static $processQueueTimer; /** @var TimingsHandler */ public static $schedulerSyncTimer; /** @var TimingsHandler */ @@ -115,6 +117,8 @@ abstract class Timings{ self::$serverCommandTimer = new TimingsHandler("Server Command"); self::$worldSaveTimer = new TimingsHandler("World Save"); self::$generationTimer = new TimingsHandler("World Generation"); + self::$populationTimer = new TimingsHandler("World Population"); + self::$generationCallbackTimer = new TimingsHandler("World Generation Callback"); self::$permissibleCalculationTimer = new TimingsHandler("Permissible Calculation"); self::$permissionDefaultTimer = new TimingsHandler("Default Permission Calculation"); @@ -130,7 +134,6 @@ abstract class Timings{ self::$timerEntityAIMove = new TimingsHandler("** livingEntityAIMove"); self::$timerEntityTickRest = new TimingsHandler("** livingEntityTickRest"); - self::$processQueueTimer = new TimingsHandler("processQueue"); self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer); self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks"); diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index f19d9d623..a95c24dae 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -612,16 +612,16 @@ class Level implements ChunkManager, Metadatable{ $this->timings->entityTick->stopTiming(); $this->timings->tileEntityTick->startTiming(); + Timings::$tickTileEntityTimer->startTiming(); //Update tiles that need update if(count($this->updateTiles) > 0){ - //Timings::$tickTileEntityTimer->startTiming(); foreach($this->updateTiles as $id => $tile){ if($tile->onUpdate() !== true){ unset($this->updateTiles[$id]); } } - //Timings::$tickTileEntityTimer->stopTiming(); } + Timings::$tickTileEntityTimer->stopTiming(); $this->timings->tileEntityTick->stopTiming(); $this->timings->doTickTiles->startTiming(); @@ -1897,7 +1897,7 @@ class Level implements ChunkManager, Metadatable{ } public function generateChunkCallback($x, $z, FullChunk $chunk){ - Timings::$generationTimer->startTiming(); + Timings::$generationCallbackTimer->startTiming(); if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){ $oldChunk = $this->getChunk($x, $z, false); for($xx = -1; $xx <= 1; ++$xx){ @@ -1918,7 +1918,7 @@ class Level implements ChunkManager, Metadatable{ $chunk->setProvider($this->provider); $this->setChunk($x, $z, $chunk); } - Timings::$generationTimer->stopTiming(); + Timings::$generationCallbackTimer->stopTiming(); } public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){ @@ -2150,6 +2150,8 @@ class Level implements ChunkManager, Metadatable{ return true; } + $this->timings->syncChunkLoadTimer->startTiming(); + $this->cancelUnloadChunkRequest($x, $z); $chunk = $this->provider->getChunk($x, $z, $generate); @@ -2157,14 +2159,13 @@ class Level implements ChunkManager, Metadatable{ $this->chunks[$index] = $chunk; $chunk->initChunk(); }else{ - $this->timings->syncChunkLoadTimer->startTiming(); $this->provider->loadChunk($x, $z, $generate); - $this->timings->syncChunkLoadTimer->stopTiming(); if(($chunk = $this->provider->getChunk($x, $z)) !== null){ $this->chunks[$index] = $chunk; $chunk->initChunk(); }else{ + $this->timings->syncChunkLoadTimer->stopTiming(); return false; } } @@ -2173,11 +2174,14 @@ class Level implements ChunkManager, Metadatable{ $this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated())); }else{ $this->unloadChunk($x, $z, false); + $this->timings->syncChunkLoadTimer->stopTiming(); return false; } $chunk->setChanged(false); + $this->timings->syncChunkLoadTimer->stopTiming(); + return true; } @@ -2408,16 +2412,15 @@ class Level implements ChunkManager, Metadatable{ return false; } - Timings::$generationTimer->startTiming(); - if(!$this->getChunk($x, $z, true)->isPopulated()){ + Timings::$populationTimer->startTiming(); + $chunk = $this->getChunk($x, $z, false); + if($chunk === null or !$chunk->isPopulated()){ $populate = true; for($xx = -1; $xx <= 1; ++$xx){ for($zz = -1; $zz <= 1; ++$zz){ if(isset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)])){ $populate = false; - }elseif(!$this->getChunk($x + $xx, $z + $zz, true)->isGenerated()){ - $populate = false; - $this->generateChunk($x + $xx, $z + $zz, $force); + break; } } } @@ -2430,17 +2433,17 @@ class Level implements ChunkManager, Metadatable{ $this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true; } } - $task = new PopulationTask($this, $this->getChunk($x, $z, true)); + $task = new PopulationTask($this, $x, $z, $chunk); $this->server->getScheduler()->scheduleAsyncTask($task); } - Timings::$generationTimer->stopTiming(); + Timings::$populationTimer->stopTiming(); return false; } - Timings::$generationTimer->stopTiming(); + Timings::$populationTimer->stopTiming(); return false; } - Timings::$generationTimer->stopTiming(); + Timings::$populationTimer->stopTiming(); return true; } diff --git a/src/pocketmine/level/format/FullChunk.php b/src/pocketmine/level/format/FullChunk.php index ac073e6cf..5406b4f61 100644 --- a/src/pocketmine/level/format/FullChunk.php +++ b/src/pocketmine/level/format/FullChunk.php @@ -346,4 +346,13 @@ interface FullChunk{ */ public static function fromFastBinary($data, LevelProvider $provider = null); + /** + * @param int $chunkX + * @param int $chunkZ + * @param LevelProvider $provider + * + * @return FullChunk + */ + public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null); + } \ 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 3f13c593a..aa525e74b 100644 --- a/src/pocketmine/level/format/anvil/Anvil.php +++ b/src/pocketmine/level/format/anvil/Anvil.php @@ -105,6 +105,10 @@ class Anvil extends McRegion{ $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 Compound("", [ "Y" => new Byte("Y", $Y), diff --git a/src/pocketmine/level/format/anvil/Chunk.php b/src/pocketmine/level/format/anvil/Chunk.php index 7ad6ec951..1a255a099 100644 --- a/src/pocketmine/level/format/anvil/Chunk.php +++ b/src/pocketmine/level/format/anvil/Chunk.php @@ -40,7 +40,11 @@ class Chunk extends BaseChunk{ protected $nbt; public function __construct($level, Compound $nbt){ - $this->nbt = $nbt; + if($nbt === null){ + $this->provider = $level; + $this->nbt = new Compound("Level", []); + return; + } if(!isset($this->nbt->Entities) or !($this->nbt->Entities instanceof Enum)){ $this->nbt->Entities = new Enum("Entities", []); @@ -63,11 +67,11 @@ class Chunk extends BaseChunk{ } if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){ - $this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"))); + $this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, 0)); } if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArray)){ - $this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); + $this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 0)); } $sections = []; @@ -287,4 +291,32 @@ class Chunk extends BaseChunk{ 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; + + $chunk->heightMap = array_fill(0, 256, 0); + $chunk->biomeColors = array_fill(0, 256, 0); + + $chunk->nbt->V = new Byte("V", 1); + $chunk->nbt->InhabitedTime = new Long("InhabitedTime", 0); + $chunk->nbt->TerrainGenerated = new Byte("TerrainGenerated", 0); + $chunk->nbt->TerrainPopulated = new Byte("TerrainPopulated", 0); + $chunk->nbt->LightPopulated = new Byte("LightPopulated", 0); + + return $chunk; + }catch(\Exception $e){ + return null; + } + } } \ 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 934e85dc4..265e62889 100644 --- a/src/pocketmine/level/format/anvil/RegionLoader.php +++ b/src/pocketmine/level/format/anvil/RegionLoader.php @@ -57,33 +57,4 @@ class RegionLoader extends \pocketmine\level\format\mcregion\RegionLoader{ protected function unserializeChunk($data){ return Chunk::fromBinary($data, $this->levelProvider); } - - public function generateChunk($x, $z){ - $nbt = new Compound("Level", []); - $nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x); - $nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z); - $nbt->LastUpdate = new Long("LastUpdate", 0); - $nbt->LightPopulated = new Byte("LightPopulated", 0); - $nbt->TerrainPopulated = new Byte("TerrainPopulated", 0); - $nbt->V = new Byte("V", self::VERSION); - $nbt->InhabitedTime = new Long("InhabitedTime", 0); - $biomes = str_repeat(Binary::writeByte(-1), 256); - $nbt->Biomes = new ByteArray("Biomes", $biomes); - $nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 156, Binary::readInt("\x00\x85\xb2\x4a"))); - $nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); - $nbt->Sections = new Enum("Sections", []); - $nbt->Sections->setTagType(NBT::TAG_Compound); - $nbt->Entities = new Enum("Entities", []); - $nbt->Entities->setTagType(NBT::TAG_Compound); - $nbt->TileEntities = new Enum("TileEntities", []); - $nbt->TileEntities->setTagType(NBT::TAG_Compound); - $nbt->TileTicks = new Enum("TileTicks", []); - $nbt->TileTicks->setTagType(NBT::TAG_Compound); - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new Compound("", ["Level" => $nbt])); - $chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, RegionLoader::$COMPRESSION_LEVEL); - $this->saveChunk($x, $z, $chunkData); - } - } \ 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 9d92b8ae3..3c5b629ad 100644 --- a/src/pocketmine/level/format/leveldb/Chunk.php +++ b/src/pocketmine/level/format/leveldb/Chunk.php @@ -339,4 +339,19 @@ class Chunk extends BaseFullChunk{ ($this->isLightPopulated() ? 0x04 : 0) | ($this->isPopulated() ? 0x02 : 0) | ($this->isGenerated() ? 0x01 : 0) ); } + + /** + * @param int $chunkX + * @param int $chunkZ + * @param LevelProvider $provider + * + * @return Chunk + */ + public static function getEmptyChunk($chunkX, $chunkZ, LevelProvider $provider = null){ + try{ + return new Chunk($provider instanceof LevelProvider ? $provider : LevelDB::class, $chunkX, $chunkZ, str_repeat("\x00", 16384 * (2 + 1 + 1 + 1) + 256 + 1024)); + }catch(\Exception $e){ + return null; + } + } } diff --git a/src/pocketmine/level/format/leveldb/LevelDB.php b/src/pocketmine/level/format/leveldb/LevelDB.php index 2aca23916..080d11953 100644 --- a/src/pocketmine/level/format/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/leveldb/LevelDB.php @@ -197,10 +197,13 @@ class LevelDB extends BaseLevelProvider{ } $this->level->timings->syncChunkLoadDataTimer->startTiming(); - $chunk = $this->readChunk($chunkX, $chunkZ, $create); //generate empty chunk if not loaded + $chunk = $this->readChunk($chunkX, $chunkZ, $create); + if($chunk === null and $create){ + $chunk = Chunk::getEmptyChunk($chunkX, $chunkZ, $this); + } $this->level->timings->syncChunkLoadDataTimer->stopTiming(); - if($chunk instanceof Chunk){ + if($chunk !== null){ $this->chunks[$index] = $chunk; return true; }else{ @@ -211,15 +214,14 @@ class LevelDB extends BaseLevelProvider{ /** * @param $chunkX * @param $chunkZ - * @param bool $create * * @return Chunk */ - private function readChunk($chunkX, $chunkZ, $create = false){ + private function readChunk($chunkX, $chunkZ){ $index = LevelDB::chunkIndex($chunkX, $chunkZ); if(!$this->chunkExists($chunkX, $chunkZ) or ($data = $this->db->get($index . "\x30")) === false){ - return $create ? $this->generateChunk($chunkX, $chunkZ) : null; + return null; } $flags = $this->db->get($index . "f"); diff --git a/src/pocketmine/level/format/mcregion/Chunk.php b/src/pocketmine/level/format/mcregion/Chunk.php index 81d1e6bf9..909b79431 100644 --- a/src/pocketmine/level/format/mcregion/Chunk.php +++ b/src/pocketmine/level/format/mcregion/Chunk.php @@ -30,6 +30,7 @@ use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\Enum; use pocketmine\nbt\tag\Int; use pocketmine\nbt\tag\IntArray; +use pocketmine\nbt\tag\Long; use pocketmine\Player; use pocketmine\utils\Binary; @@ -40,6 +41,7 @@ class Chunk extends BaseFullChunk{ public function __construct($level, Compound $nbt = null){ if($nbt === null){ + $this->provider = $level; $this->nbt = new Compound("Level", []); return; } @@ -68,11 +70,11 @@ class Chunk extends BaseFullChunk{ } if(!isset($this->nbt->BiomeColors) or !($this->nbt->BiomeColors instanceof IntArray)){ - $this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x01\x85\xb2\x4a"))); + $this->nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, 0)); } if(!isset($this->nbt->HeightMap) or !($this->nbt->HeightMap instanceof IntArray)){ - $this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); + $this->nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 0)); } if(!isset($this->nbt->Blocks)){ @@ -410,4 +412,37 @@ class Chunk extends BaseFullChunk{ 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 = $chunk->data; + $chunk->blockLight = $chunk->data; + + $chunk->heightMap = array_fill(0, 256, 0); + $chunk->biomeColors = array_fill(0, 256, 0); + + $chunk->nbt->V = new Byte("V", 1); + $chunk->nbt->InhabitedTime = new Long("InhabitedTime", 0); + $chunk->nbt->TerrainGenerated = new Byte("TerrainGenerated", 0); + $chunk->nbt->TerrainPopulated = new Byte("TerrainPopulated", 0); + $chunk->nbt->LightPopulated = new Byte("LightPopulated", 0); + + return $chunk; + }catch(\Exception $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 49779af6e..d8fd4eb03 100644 --- a/src/pocketmine/level/format/mcregion/McRegion.php +++ b/src/pocketmine/level/format/mcregion/McRegion.php @@ -192,10 +192,13 @@ class McRegion extends BaseLevelProvider{ self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); $this->loadRegion($regionX, $regionZ); $this->level->timings->syncChunkLoadDataTimer->startTiming(); - $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32, $create); //generate empty chunk if not loaded + $chunk = $this->getRegion($regionX, $regionZ)->readChunk($chunkX - $regionX * 32, $chunkZ - $regionZ * 32); + if($chunk === null and $create){ + $chunk = $this->getEmptyChunk($chunkX, $chunkZ); + } $this->level->timings->syncChunkLoadDataTimer->stopTiming(); - if($chunk instanceof FullChunk){ + if($chunk !== null){ $this->chunks[$index] = $chunk; return true; }else{ @@ -203,6 +206,10 @@ class McRegion extends BaseLevelProvider{ } } + public function getEmptyChunk($chunkX, $chunkZ){ + return Chunk::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)){ diff --git a/src/pocketmine/level/format/mcregion/RegionLoader.php b/src/pocketmine/level/format/mcregion/RegionLoader.php index 80599ccaa..21fdca34c 100644 --- a/src/pocketmine/level/format/mcregion/RegionLoader.php +++ b/src/pocketmine/level/format/mcregion/RegionLoader.php @@ -83,7 +83,7 @@ class RegionLoader{ return !($this->locationTable[$index][0] === 0 or $this->locationTable[$index][1] === 0); } - public function readChunk($x, $z, $generate = true, $forward = false){ + public function readChunk($x, $z){ $index = self::getChunkOffset($x, $z); if($index < 0 or $index >= 4096){ return null; @@ -92,16 +92,7 @@ class RegionLoader{ $this->lastUsed = time(); if(!$this->isChunkGenerated($index)){ - if($generate === true){ - //Allocate space - $this->locationTable[$index][0] = ++$this->lastSector; - $this->locationTable[$index][1] = 1; - fseek($this->filePointer, $this->locationTable[$index][0] << 12); - fwrite($this->filePointer, str_pad(Binary::writeInt(0) . chr(self::COMPRESSION_ZLIB), 4096, "\x00", STR_PAD_RIGHT)); - $this->writeLocationIndex($index); - }else{ - return null; - } + return null; } fseek($this->filePointer, $this->locationTable[$index][0] << 12); @@ -114,10 +105,7 @@ class RegionLoader{ $this->locationTable[$index][1] = 1; MainLogger::getLogger()->error("Corrupted chunk header detected"); } - $this->generateChunk($x, $z); - fseek($this->filePointer, $this->locationTable[$index][0] << 12); - $length = Binary::readInt(fread($this->filePointer, 4)); - $compression = ord(fgetc($this->filePointer)); + return null; } if($length > ($this->locationTable[$index][1] << 12)){ //Invalid chunk, bigger than defined number of sectors @@ -126,19 +114,14 @@ class RegionLoader{ $this->writeLocationIndex($index); }elseif($compression !== self::COMPRESSION_ZLIB and $compression !== self::COMPRESSION_GZIP){ MainLogger::getLogger()->error("Invalid compression type"); - return null; } $chunk = $this->unserializeChunk(fread($this->filePointer, $length - 1)); if($chunk instanceof FullChunk){ return $chunk; - }elseif($forward === false){ - MainLogger::getLogger()->error("Corrupted chunk detected"); - $this->generateChunk($x, $z); - - return $this->readChunk($x, $z, $generate, true); }else{ + MainLogger::getLogger()->error("Corrupted chunk detected"); return null; } } @@ -151,43 +134,6 @@ class RegionLoader{ return $this->isChunkGenerated(self::getChunkOffset($x, $z)); } - public function generateChunk($x, $z){ - $nbt = new Compound("Level", []); - $nbt->xPos = new Int("xPos", ($this->getX() * 32) + $x); - $nbt->zPos = new Int("zPos", ($this->getZ() * 32) + $z); - $nbt->LastUpdate = new Long("LastUpdate", 0); - $nbt->LightPopulated = new Byte("LightPopulated", 0); - $nbt->TerrainPopulated = new Byte("TerrainPopulated", 0); - $nbt->V = new Byte("V", self::VERSION); - $nbt->InhabitedTime = new Long("InhabitedTime", 0); - $biomes = str_repeat(Binary::writeByte(-1), 256); - $nbt->Biomes = new ByteArray("Biomes", $biomes); - $nbt->HeightMap = new IntArray("HeightMap", array_fill(0, 256, 127)); - $nbt->BiomeColors = new IntArray("BiomeColors", array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"))); - - $half = str_repeat("\x00", 16384); - $full = $half . $half; - $nbt->Blocks = new ByteArray("Blocks", $full); - $nbt->Data = new ByteArray("Data", $half); - $nbt->SkyLight = new ByteArray("SkyLight", str_repeat("\xff", 16384)); - $nbt->BlockLight = new ByteArray("BlockLight", $half); - - $nbt->Entities = new Enum("Entities", []); - $nbt->Entities->setTagType(NBT::TAG_Compound); - $nbt->TileEntities = new Enum("TileEntities", []); - $nbt->TileEntities->setTagType(NBT::TAG_Compound); - $nbt->TileTicks = new Enum("TileTicks", []); - $nbt->TileTicks->setTagType(NBT::TAG_Compound); - $writer = new NBT(NBT::BIG_ENDIAN); - $nbt->setName("Level"); - $writer->setData(new Compound("", ["Level" => $nbt])); - $chunkData = $writer->writeCompressed(ZLIB_ENCODING_DEFLATE, self::$COMPRESSION_LEVEL); - - if($chunkData !== false){ - $this->saveChunk($x, $z, $chunkData); - } - } - protected function saveChunk($x, $z, $chunkData){ $length = strlen($chunkData) + 1; if($length + 4 > self::MAX_SECTOR_LENGTH){ diff --git a/src/pocketmine/level/generator/PopulationTask.php b/src/pocketmine/level/generator/PopulationTask.php index 22ac23e83..3879c6434 100644 --- a/src/pocketmine/level/generator/PopulationTask.php +++ b/src/pocketmine/level/generator/PopulationTask.php @@ -36,33 +36,37 @@ class PopulationTask extends AsyncTask{ public $state; public $levelId; public $chunk; + public $chunkX; + public $chunkZ; public $chunkClass; - public $chunk00; - public $chunk01; - public $chunk02; - public $chunk10; + public $chunk0; + public $chunk1; + public $chunk2; + public $chunk3; //center chunk - public $chunk12; - public $chunk20; - public $chunk21; - public $chunk22; + public $chunk5; + public $chunk6; + public $chunk7; + public $chunk8; - public function __construct(Level $level, FullChunk $chunk){ + public function __construct(Level $level, $chunkX, $chunkZ, FullChunk $chunk = null){ $this->state = true; $this->levelId = $level->getId(); - $this->chunk = $chunk->toFastBinary(); + $this->chunk = $chunk !== null ? $chunk->toFastBinary() : null; + $this->chunkX = $chunkX; + $this->chunkZ = $chunkZ; $this->chunkClass = get_class($chunk); - $this->chunk00 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() - 1, true)->toFastBinary(); - $this->chunk01 = $level->getChunk($chunk->getX() - 1, $chunk->getZ(), true)->toFastBinary(); - $this->chunk02 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() + 1, true)->toFastBinary(); - $this->chunk10 = $level->getChunk($chunk->getX(), $chunk->getZ() - 1, true)->toFastBinary(); - - $this->chunk12 = $level->getChunk($chunk->getX(), $chunk->getZ() + 1, true)->toFastBinary(); - $this->chunk20 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() - 1, true)->toFastBinary(); - $this->chunk21 = $level->getChunk($chunk->getX() + 1, $chunk->getZ(), true)->toFastBinary(); - $this->chunk22 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() + 1, true)->toFastBinary(); + for($i = 0; $i < 9; ++$i){ + if($i === 4){ + continue; + } + $xx = -1 + $i % 3; + $zz = -1 + (int) ($i / 3); + $ck = $level->getChunk($chunkX + $xx, $chunkZ + $zz, false); + $this->{"chunk$i"} = $ck !== null ? $ck->toFastBinary() : null; + } } public function onRun(){ @@ -75,20 +79,29 @@ class PopulationTask extends AsyncTask{ return; } + $chunkX = $this->chunkX; + $chunkZ = $this->chunkZ; + /** @var FullChunk[] $chunks */ $chunks = []; /** @var FullChunk $chunkC */ $chunkC = $this->chunkClass; - $chunks[0] = $chunkC::fromFastBinary($this->chunk00); - $chunks[1] = $chunkC::fromFastBinary($this->chunk01); - $chunks[2] = $chunkC::fromFastBinary($this->chunk02); - $chunks[3] = $chunkC::fromFastBinary($this->chunk10); - $chunk = $chunkC::fromFastBinary($this->chunk); - $chunks[5] = $chunkC::fromFastBinary($this->chunk12); - $chunks[6] = $chunkC::fromFastBinary($this->chunk20); - $chunks[7] = $chunkC::fromFastBinary($this->chunk21); - $chunks[8] = $chunkC::fromFastBinary($this->chunk22); + $chunk = $this->chunk !== null ? $chunkC::fromFastBinary($this->chunk) : $chunkC::getEmptyChunk($chunkX, $chunkZ); + + for($i = 0; $i < 9; ++$i){ + if($i === 4){ + continue; + } + $xx = -1 + $i % 3; + $zz = -1 + (int) ($i / 3); + $ck = $this->{"chunk$i"}; + if($ck === null){ + $chunks[$i] = $chunkC::getEmptyChunk($chunkX + $xx, $chunkZ + $zz); + }else{ + $chunks[$i] = $chunkC::fromFastBinary($ck); + } + } if($chunk === null){ //TODO error @@ -96,10 +109,18 @@ class PopulationTask extends AsyncTask{ } $manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk); + if(!$chunk->isGenerated()){ + $generator->generateChunk($chunk->getX(), $chunk->getZ()); + $chunk->setGenerated(); + } foreach($chunks as $c){ if($c !== null){ $manager->setChunk($c->getX(), $c->getZ(), $c); + if(!$c->isGenerated()){ + $generator->generateChunk($c->getX(), $c->getZ()); + $c->setGenerated(); + } } } @@ -128,15 +149,13 @@ class PopulationTask extends AsyncTask{ $manager->cleanChunks(); - $this->chunk00 = $chunks[0] !== null ? $chunks[0]->toFastBinary() : null; - $this->chunk01 = $chunks[1] !== null ? $chunks[1]->toFastBinary() : null; - $this->chunk02 = $chunks[2] !== null ? $chunks[2]->toFastBinary() : null; - $this->chunk10 = $chunks[3] !== null ? $chunks[3]->toFastBinary() : null; + for($i = 0; $i < 9; ++$i){ + if($i === 4){ + continue; + } - $this->chunk12 = $chunks[5] !== null ? $chunks[5]->toFastBinary() : null; - $this->chunk20 = $chunks[6] !== null ? $chunks[6]->toFastBinary() : null; - $this->chunk21 = $chunks[7] !== null ? $chunks[7]->toFastBinary() : null; - $this->chunk22 = $chunks[8] !== null ? $chunks[8]->toFastBinary() : null; + $this->{"chunk$i"} = $chunks[$i] !== null ? $chunks[$i]->toFastBinary() : null; + } } public function onCompletion(Server $server){ @@ -146,33 +165,28 @@ class PopulationTask extends AsyncTask{ $level->registerGenerator(); return; } - /** @var FullChunk[] $chunks */ - $chunks = []; + /** @var FullChunk $chunkC */ $chunkC = $this->chunkClass; - $chunks[0] = $chunkC::fromFastBinary($this->chunk00, $level->getProvider()); - $chunks[1] = $chunkC::fromFastBinary($this->chunk01, $level->getProvider()); - $chunks[2] = $chunkC::fromFastBinary($this->chunk02, $level->getProvider()); - $chunks[3] = $chunkC::fromFastBinary($this->chunk10, $level->getProvider()); $chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider()); - $chunks[5] = $chunkC::fromFastBinary($this->chunk12, $level->getProvider()); - $chunks[6] = $chunkC::fromFastBinary($this->chunk20, $level->getProvider()); - $chunks[7] = $chunkC::fromFastBinary($this->chunk21, $level->getProvider()); - $chunks[8] = $chunkC::fromFastBinary($this->chunk22, $level->getProvider()); - - foreach($chunks as $c){ - if($c !== null){ - $level->generateChunkCallback($c->getX(), $c->getZ(), $c); - } - } - if($chunk === null){ //TODO error return; } + for($i = 0; $i < 9; ++$i){ + if($i === 4){ + continue; + } + $c = $this->{"chunk$i"}; + if($c !== null){ + $c = $chunkC::fromFastBinary($c); + $level->generateChunkCallback($c->getX(), $c->getZ(), $c); + } + } + $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); } }