diff --git a/src/pocketmine/level/format/io/BaseLevelProvider.php b/src/pocketmine/level/format/io/BaseLevelProvider.php index 24fc3e86b..962910120 100644 --- a/src/pocketmine/level/format/io/BaseLevelProvider.php +++ b/src/pocketmine/level/format/io/BaseLevelProvider.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\level\format\io; +use pocketmine\level\format\Chunk; use pocketmine\level\generator\Generator; use pocketmine\level\Level; use pocketmine\level\LevelException; @@ -38,6 +39,8 @@ abstract class BaseLevelProvider implements LevelProvider{ protected $path; /** @var CompoundTag */ protected $levelData; + /** @var Chunk[] */ + protected $chunks = []; public function __construct(Level $level, string $path){ $this->level = $level; @@ -124,4 +127,106 @@ abstract class BaseLevelProvider implements LevelProvider{ $buffer = $nbt->writeCompressed(); file_put_contents($this->getPath() . "level.dat", $buffer); } + + public function getChunk(int $chunkX, int $chunkZ, bool $create = false){ + $index = Level::chunkHash($chunkX, $chunkZ); + if(isset($this->chunks[$index])){ + return $this->chunks[$index]; + }else{ + $this->loadChunk($chunkX, $chunkZ, $create); + + return $this->chunks[$index] ?? null; + } + } + + public function isChunkLoaded(int $chunkX, int $chunkZ) : bool{ + return isset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); + } + + public function getLoadedChunks() : array{ + return $this->chunks; + } + + public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : bool{ + $index = Level::chunkHash($chunkX, $chunkZ); + if(isset($this->chunks[$index])){ + return true; + } + + $this->level->timings->syncChunkLoadDataTimer->startTiming(); + $chunk = $this->readChunk($chunkX, $chunkZ); + if($chunk === null and $create){ + $chunk = new Chunk($chunkX, $chunkZ); + } + $this->level->timings->syncChunkLoadDataTimer->stopTiming(); + + if($chunk !== null){ + $this->chunks[$index] = $chunk; + + return true; + }else{ + return false; + } + } + + public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk){ + $chunk->setX($chunkX); + $chunk->setZ($chunkZ); + + if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) and $this->chunks[$index] !== $chunk){ + $this->unloadChunk($chunkX, $chunkZ, false); + } + + $this->chunks[$index] = $chunk; + } + + public function saveChunks(){ + foreach($this->chunks as $chunk){ + $this->saveChunk($chunk->getX(), $chunk->getZ()); + } + } + + public function saveChunk(int $chunkX, int $chunkZ) : bool{ + if($this->isChunkLoaded($chunkX, $chunkZ)){ + $chunk = $this->getChunk($chunkX, $chunkZ); + if(!$chunk->isGenerated()){ + throw new \InvalidStateException("Cannot save un-generated chunk"); + } + $this->writeChunk($chunk); + + return true; + } + + return false; + } + + public function unloadChunk(int $chunkX, int $chunkZ, bool $safe = true) : bool{ + $chunk = $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] ?? null; + if($chunk instanceof Chunk and $chunk->unload($safe)){ + unset($this->chunks[$index]); + return true; + } + + return false; + } + + public function unloadChunks(){ + foreach($this->chunks as $chunk){ + $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); + } + $this->chunks = []; + } + + public function isChunkPopulated(int $chunkX, int $chunkZ) : bool{ + $chunk = $this->getChunk($chunkX, $chunkZ); + if($chunk !== null){ + return $chunk->isPopulated(); + }else{ + return false; + } + } + + abstract protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk; + + abstract protected function writeChunk(Chunk $chunk) : void; } diff --git a/src/pocketmine/level/format/io/leveldb/LevelDB.php b/src/pocketmine/level/format/io/leveldb/LevelDB.php index 3c58396dd..a597be7fc 100644 --- a/src/pocketmine/level/format/io/leveldb/LevelDB.php +++ b/src/pocketmine/level/format/io/leveldb/LevelDB.php @@ -74,9 +74,6 @@ class LevelDB extends BaseLevelProvider{ public const CURRENT_LEVEL_CHUNK_VERSION = 7; public const CURRENT_LEVEL_SUBCHUNK_VERSION = 0; - /** @var Chunk[] */ - protected $chunks = []; - /** @var \LevelDB */ protected $db; @@ -249,13 +246,6 @@ class LevelDB extends BaseLevelProvider{ file_put_contents($this->getPath() . "level.dat", Binary::writeLInt(self::CURRENT_STORAGE_VERSION) . Binary::writeLInt(strlen($buffer)) . $buffer); } - public function unloadChunks(){ - foreach($this->chunks as $chunk){ - $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); - } - $this->chunks = []; - } - public function getGenerator() : string{ return (string) $this->levelData["generatorName"]; } @@ -272,48 +262,13 @@ class LevelDB extends BaseLevelProvider{ $this->levelData->setInt("Difficulty", $difficulty); //yes, this is intended! (in PE: int, PC: byte) } - public function getLoadedChunks() : array{ - return $this->chunks; - } - - public function isChunkLoaded(int $x, int $z) : bool{ - return isset($this->chunks[Level::chunkHash($x, $z)]); - } - - public function saveChunks(){ - foreach($this->chunks as $chunk){ - $this->saveChunk($chunk->getX(), $chunk->getZ()); - } - } - - public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : bool{ - if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)])){ - return true; - } - - $this->level->timings->syncChunkLoadDataTimer->startTiming(); - $chunk = $this->readChunk($chunkX, $chunkZ); - if($chunk === null and $create){ - $chunk = new Chunk($chunkX, $chunkZ); - } - $this->level->timings->syncChunkLoadDataTimer->stopTiming(); - - if($chunk !== null){ - $this->chunks[$index] = $chunk; - - return true; - }else{ - return false; - } - } - /** * @param int $chunkX * @param int $chunkZ * * @return Chunk|null */ - private function readChunk($chunkX, $chunkZ){ + protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk{ $index = LevelDB::chunkIndex($chunkX, $chunkZ); if(!$this->chunkExists($chunkX, $chunkZ)){ @@ -484,7 +439,7 @@ class LevelDB extends BaseLevelProvider{ } } - private function writeChunk(Chunk $chunk){ + protected function writeChunk(Chunk $chunk) : void{ $index = LevelDB::chunkIndex($chunk->getX(), $chunk->getZ()); $this->db->put($index . self::TAG_VERSION, chr(self::CURRENT_LEVEL_CHUNK_VERSION)); @@ -557,49 +512,6 @@ class LevelDB extends BaseLevelProvider{ } } - public function unloadChunk(int $x, int $z, bool $safe = true) : bool{ - $chunk = $this->chunks[$index = Level::chunkHash($x, $z)] ?? null; - if($chunk instanceof Chunk and $chunk->unload($safe)){ - unset($this->chunks[$index]); - - return true; - } - - return false; - } - - public function saveChunk(int $chunkX, int $chunkZ) : bool{ - if($this->isChunkLoaded($chunkX, $chunkZ)){ - $chunk = $this->getChunk($chunkX, $chunkZ); - if(!$chunk->isGenerated()){ - throw new \InvalidStateException("Cannot save un-generated chunk"); - } - $this->writeChunk($chunk); - - return true; - } - - return false; - } - - /** - * @param int $chunkX - * @param int $chunkZ - * @param bool $create - * - * @return Chunk|null - */ - public function getChunk(int $chunkX, int $chunkZ, bool $create = false){ - $index = Level::chunkHash($chunkX, $chunkZ); - if(isset($this->chunks[$index])){ - return $this->chunks[$index]; - }else{ - $this->loadChunk($chunkX, $chunkZ, $create); - - return $this->chunks[$index] ?? null; - } - } - /** * @return \LevelDB */ @@ -607,17 +519,6 @@ class LevelDB extends BaseLevelProvider{ return $this->db; } - public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk){ - $chunk->setX($chunkX); - $chunk->setZ($chunkZ); - - if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) and $this->chunks[$index] !== $chunk){ - $this->unloadChunk($chunkX, $chunkZ, false); - } - - $this->chunks[$index] = $chunk; - } - public static function chunkIndex(int $chunkX, int $chunkZ) : string{ return Binary::writeLInt($chunkX) . Binary::writeLInt($chunkZ); } @@ -630,15 +531,6 @@ class LevelDB extends BaseLevelProvider{ return $this->chunkExists($chunkX, $chunkZ) and ($chunk = $this->getChunk($chunkX, $chunkZ, false)) !== null; } - public function isChunkPopulated(int $chunkX, int $chunkZ) : bool{ - $chunk = $this->getChunk($chunkX, $chunkZ); - if($chunk instanceof Chunk){ - return $chunk->isPopulated(); - }else{ - return false; - } - } - public function close(){ $this->unloadChunks(); $this->db->close(); diff --git a/src/pocketmine/level/format/io/region/McRegion.php b/src/pocketmine/level/format/io/region/McRegion.php index 4d31e7eec..d0eccb1d4 100644 --- a/src/pocketmine/level/format/io/region/McRegion.php +++ b/src/pocketmine/level/format/io/region/McRegion.php @@ -44,9 +44,6 @@ class McRegion extends BaseLevelProvider{ /** @var RegionLoader[] */ protected $regions = []; - /** @var Chunk[] */ - protected $chunks = []; - /** * @param Chunk $chunk * @@ -283,105 +280,6 @@ class McRegion extends BaseLevelProvider{ $this->levelData->setByte("Difficulty", $difficulty); } - public function getChunk(int $chunkX, int $chunkZ, bool $create = false){ - $index = Level::chunkHash($chunkX, $chunkZ); - if(isset($this->chunks[$index])){ - return $this->chunks[$index]; - }else{ - $this->loadChunk($chunkX, $chunkZ, $create); - - return $this->chunks[$index] ?? null; - } - } - - public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk){ - self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); - $this->loadRegion($regionX, $regionZ); - - $chunk->setX($chunkX); - $chunk->setZ($chunkZ); - - - if(isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) and $this->chunks[$index] !== $chunk){ - $this->unloadChunk($chunkX, $chunkZ, false); - } - - $this->chunks[$index] = $chunk; - } - - public function saveChunk(int $chunkX, int $chunkZ) : bool{ - if($this->isChunkLoaded($chunkX, $chunkZ)){ - $chunk = $this->getChunk($chunkX, $chunkZ); - if(!$chunk->isGenerated()){ - throw new \InvalidStateException("Cannot save un-generated chunk"); - } - $this->getRegion($chunkX >> 5, $chunkZ >> 5)->writeChunk($chunkX & 0x1f, $chunkZ & 0x1f, $this->nbtSerialize($chunk)); - - return true; - } - - return false; - } - - public function saveChunks(){ - foreach($this->chunks as $chunk){ - $this->saveChunk($chunk->getX(), $chunk->getZ()); - } - } - - public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : bool{ - $index = Level::chunkHash($chunkX, $chunkZ); - if(isset($this->chunks[$index])){ - return true; - } - $regionX = $regionZ = null; - self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); - assert(is_int($regionX) and is_int($regionZ)); - - $this->loadRegion($regionX, $regionZ); - $this->level->timings->syncChunkLoadDataTimer->startTiming(); - - $chunk = null; - - $chunkData = $this->getRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f); - if($chunkData !== null){ - $chunk = $this->nbtDeserialize($chunkData); - } - - if($chunk === null and $create){ - $chunk = new Chunk($chunkX, $chunkZ); - } - $this->level->timings->syncChunkLoadDataTimer->stopTiming(); - - if($chunk !== null){ - $this->chunks[$index] = $chunk; - return true; - }else{ - return false; - } - } - - public function unloadChunk(int $chunkX, int $chunkZ, bool $safe = true) : bool{ - $chunk = $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] ?? null; - if($chunk instanceof Chunk and $chunk->unload($safe)){ - unset($this->chunks[$index]); - return true; - } - - return false; - } - - public function unloadChunks(){ - foreach($this->chunks as $chunk){ - $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); - } - $this->chunks = []; - } - - public function isChunkLoaded(int $chunkX, int $chunkZ) : bool{ - return isset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); - } - public function isChunkGenerated(int $chunkX, int $chunkZ) : bool{ if(($region = $this->getRegion($chunkX >> 5, $chunkZ >> 5)) !== null){ return $region->chunkExists($chunkX & 0x1f, $chunkZ & 0x1f) and $this->getChunk($chunkX & 0x1f, $chunkZ & 0x1f, true)->isGenerated(); @@ -390,19 +288,6 @@ class McRegion extends BaseLevelProvider{ return false; } - public function isChunkPopulated(int $chunkX, int $chunkZ) : bool{ - $chunk = $this->getChunk($chunkX, $chunkZ); - if($chunk !== null){ - return $chunk->isPopulated(); - }else{ - return false; - } - } - - public function getLoadedChunks() : array{ - return $this->chunks; - } - public function doGarbageCollection(){ $limit = time() - 300; foreach($this->regions as $index => $region){ @@ -480,4 +365,29 @@ class McRegion extends BaseLevelProvider{ } $this->level = null; } + + protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk{ + $regionX = $regionZ = null; + self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); + assert(is_int($regionX) and is_int($regionZ)); + + $this->loadRegion($regionX, $regionZ); + + $chunkData = $this->getRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f); + if($chunkData !== null){ + return $this->nbtDeserialize($chunkData); + } + + return null; + } + + protected function writeChunk(Chunk $chunk) : void{ + $chunkX = $chunk->getX(); + $chunkZ = $chunk->getZ(); + + self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ); + $this->loadRegion($regionX, $regionZ); + + $this->getRegion($regionX, $regionZ)->writeChunk($chunkX & 0x1f, $chunkZ & 0x1f, $this->nbtSerialize($chunk)); + } }