LevelProvider: Refactor providers to drop lots of duplicated code

This commit is contained in:
Dylan K. Taylor 2017-12-31 16:10:58 +00:00
parent a0a2ea01bc
commit f17b3b2a3b
3 changed files with 132 additions and 225 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -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));
}
}