mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 00:33:59 +00:00
Merge PR #1864: LevelProviders refactor
This commit is contained in:
commit
41873bb115
@ -345,7 +345,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelProvider $provider */
|
||||
|
||||
if(is_subclass_of($provider, LevelProvider::class, true)){
|
||||
$this->provider = new $provider($this, $path);
|
||||
$this->provider = new $provider($path);
|
||||
}else{
|
||||
throw new LevelException("Provider is not a subclass of LevelProvider");
|
||||
}
|
||||
@ -1052,8 +1052,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function saveChunks(){
|
||||
foreach($this->chunks as $chunk){
|
||||
if($chunk->hasChanged() and $chunk->isGenerated()){
|
||||
$this->provider->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
$this->provider->saveChunk($chunk->getX(), $chunk->getZ());
|
||||
$this->provider->saveChunk($chunk);
|
||||
$chunk->setChanged(false);
|
||||
}
|
||||
}
|
||||
@ -2413,6 +2412,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
if($chunk === null){
|
||||
return;
|
||||
}
|
||||
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
|
||||
$chunkHash = Level::chunkHash($chunkX, $chunkZ);
|
||||
$oldChunk = $this->getChunk($chunkX, $chunkZ, false);
|
||||
if($unload and $oldChunk !== null){
|
||||
@ -2434,7 +2437,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->provider->setChunk($chunkX, $chunkZ, $chunk);
|
||||
$this->chunks[$chunkHash] = $chunk;
|
||||
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
@ -2469,7 +2471,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkLoaded(int $x, int $z) : bool{
|
||||
return isset($this->chunks[Level::chunkHash($x, $z)]) or $this->provider->isChunkLoaded($x, $z);
|
||||
return isset($this->chunks[Level::chunkHash($x, $z)]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2552,7 +2554,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
$this->timings->syncChunkSendPrepareTimer->startTiming();
|
||||
|
||||
$chunk = $this->provider->getChunk($x, $z, false);
|
||||
$chunk = $this->chunks[$index] ?? null;
|
||||
if(!($chunk instanceof Chunk)){
|
||||
throw new ChunkException("Invalid Chunk sent");
|
||||
}
|
||||
@ -2692,7 +2694,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$this->cancelUnloadChunkRequest($x, $z);
|
||||
|
||||
$chunk = $this->provider->getChunk($x, $z, $generate);
|
||||
$this->timings->syncChunkLoadDataTimer->startTiming();
|
||||
|
||||
$chunk = $this->provider->loadChunk($x, $z, $generate);
|
||||
|
||||
$this->timings->syncChunkLoadDataTimer->stopTiming();
|
||||
|
||||
if($chunk === null){
|
||||
if($generate){
|
||||
throw new \InvalidStateException("Could not create new Chunk");
|
||||
@ -2762,28 +2769,27 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($this, $chunk));
|
||||
if($ev->isCancelled()){
|
||||
$this->timings->doChunkUnload->stopTiming();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
if($chunk !== null){
|
||||
try{
|
||||
if($trySave and $this->getAutoSave() and $chunk->isGenerated()){
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0){
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->provider->saveChunk($x, $z);
|
||||
$this->provider->saveChunk($chunk);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkUnloaded($chunk);
|
||||
}
|
||||
|
||||
$chunk->onUnload();
|
||||
}catch(\Throwable $e){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->error($this->server->getLanguage()->translateString("pocketmine.level.chunkUnloadError", [$e->getMessage()]));
|
||||
$logger->logException($e);
|
||||
}
|
||||
$this->provider->unloadChunk($x, $z, $safe);
|
||||
}catch(\Throwable $e){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->error($this->server->getLanguage()->translateString("pocketmine.level.chunkUnloadError", [$e->getMessage()]));
|
||||
$logger->logException($e);
|
||||
}
|
||||
|
||||
unset($this->chunks[$chunkHash]);
|
||||
@ -3039,12 +3045,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->provider->getLoadedChunks() as $chunk){
|
||||
if(!isset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
$this->provider->unloadChunk($chunk->getX(), $chunk->getZ(), false);
|
||||
}
|
||||
}
|
||||
|
||||
$this->provider->doGarbageCollection();
|
||||
|
||||
$this->timings->doChunkGC->stopTiming();
|
||||
|
@ -691,21 +691,9 @@ class Chunk{
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the chunk, closing entities and tiles.
|
||||
*
|
||||
* @param bool $safe Whether to check if there are still players using this chunk
|
||||
*
|
||||
* @return bool
|
||||
* Called when the chunk is unloaded, closing entities and tiles.
|
||||
*/
|
||||
public function unload(bool $safe = true) : bool{
|
||||
if($safe){
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onUnload() : void{
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
continue;
|
||||
@ -716,8 +704,6 @@ class Chunk{
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
@ -32,15 +33,12 @@ use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
|
||||
abstract class BaseLevelProvider implements LevelProvider{
|
||||
/** @var Level */
|
||||
protected $level;
|
||||
/** @var string */
|
||||
protected $path;
|
||||
/** @var CompoundTag */
|
||||
protected $levelData;
|
||||
|
||||
public function __construct(Level $level, string $path){
|
||||
$this->level = $level;
|
||||
public function __construct(string $path){
|
||||
$this->path = $path;
|
||||
if(!file_exists($this->path)){
|
||||
mkdir($this->path, 0777, true);
|
||||
@ -67,14 +65,6 @@ abstract class BaseLevelProvider implements LevelProvider{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
public function getServer(){
|
||||
return $this->level->getServer();
|
||||
}
|
||||
|
||||
public function getLevel() : Level{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return $this->levelData->getString("LevelName");
|
||||
}
|
||||
@ -124,4 +114,24 @@ abstract class BaseLevelProvider implements LevelProvider{
|
||||
$buffer = $nbt->writeCompressed();
|
||||
file_put_contents($this->getPath() . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : ?Chunk{
|
||||
$chunk = $this->readChunk($chunkX, $chunkZ);
|
||||
if($chunk === null and $create){
|
||||
$chunk = new Chunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
public function saveChunk(Chunk $chunk) : void{
|
||||
if(!$chunk->isGenerated()){
|
||||
throw new \InvalidStateException("Cannot save un-generated chunk");
|
||||
}
|
||||
$this->writeChunk($chunk);
|
||||
}
|
||||
|
||||
abstract protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk;
|
||||
|
||||
abstract protected function writeChunk(Chunk $chunk) : void;
|
||||
}
|
||||
|
@ -24,16 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
interface LevelProvider{
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct(Level $level, string $path);
|
||||
public function __construct(string $path);
|
||||
|
||||
/**
|
||||
* Returns the full provider name, like "anvil" or "mcregion", will be used to find the correct format.
|
||||
@ -88,77 +86,23 @@ interface LevelProvider{
|
||||
public function getGeneratorOptions() : array;
|
||||
|
||||
/**
|
||||
* Gets the Chunk object
|
||||
* This method must be implemented by all the level formats.
|
||||
* Saves a chunk (usually to disk).
|
||||
*
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk|null
|
||||
*/
|
||||
public function getChunk(int $chunkX, int $chunkZ, bool $create = false);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param Chunk $chunk
|
||||
*/
|
||||
public function setChunk(int $chunkX, int $chunkZ, Chunk $chunk);
|
||||
public function saveChunk(Chunk $chunk) : void;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* Loads a chunk (usually from disk storage) and returns it. If the chunk does not exist, null is returned, or an
|
||||
* empty Chunk if $create is specified.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveChunk(int $chunkX, int $chunkZ) : bool;
|
||||
|
||||
public function saveChunks();
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $create
|
||||
*
|
||||
* @return bool
|
||||
* @return null|Chunk
|
||||
*/
|
||||
public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : bool;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param bool $safe
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function unloadChunk(int $chunkX, int $chunkZ, bool $safe = true) : bool;
|
||||
|
||||
public function unloadChunks();
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkLoaded(int $chunkX, int $chunkZ) : bool;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkGenerated(int $chunkX, int $chunkZ) : bool;
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkPopulated(int $chunkX, int $chunkZ) : bool;
|
||||
public function loadChunk(int $chunkX, int $chunkZ, bool $create = false) : ?Chunk;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
@ -208,17 +152,13 @@ interface LevelProvider{
|
||||
public function setDifficulty(int $difficulty);
|
||||
|
||||
/**
|
||||
* @return Chunk[]
|
||||
* Performs garbage collection in the level provider, such as cleaning up regions in Region-based worlds.
|
||||
*/
|
||||
public function getLoadedChunks() : array;
|
||||
|
||||
public function doGarbageCollection();
|
||||
|
||||
/**
|
||||
* @return Level
|
||||
* Performs cleanups necessary when the level provider is closed and no longer needed.
|
||||
*/
|
||||
public function getLevel() : Level;
|
||||
|
||||
public function close();
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
@ -90,10 +87,9 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct(Level $level, string $path){
|
||||
public function __construct(string $path){
|
||||
self::checkForLevelDBExtension();
|
||||
|
||||
$this->level = $level;
|
||||
$this->path = $path;
|
||||
if(!file_exists($this->path)){
|
||||
mkdir($this->path, 0777, true);
|
||||
@ -249,13 +245,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 +261,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 +438,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 +511,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 +518,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);
|
||||
}
|
||||
@ -626,22 +526,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
return $this->db->get(LevelDB::chunkIndex($chunkX, $chunkZ) . self::TAG_VERSION) !== false;
|
||||
}
|
||||
|
||||
public function isChunkGenerated(int $chunkX, int $chunkZ) : bool{
|
||||
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();
|
||||
$this->level = null;
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,6 @@ class McRegion extends BaseLevelProvider{
|
||||
/** @var RegionLoader[] */
|
||||
protected $regions = [];
|
||||
|
||||
/** @var Chunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
/**
|
||||
* @param Chunk $chunk
|
||||
*
|
||||
@ -283,126 +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();
|
||||
}
|
||||
|
||||
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){
|
||||
@ -473,11 +350,34 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
public function close(){
|
||||
$this->unloadChunks();
|
||||
foreach($this->regions as $index => $region){
|
||||
$region->close();
|
||||
unset($this->regions[$index]);
|
||||
}
|
||||
$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));
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 316fd5606fef1191e7f228d3156dd1518c33aba4
|
||||
Subproject commit 726930e597fd4dd4c9941a8e3dfdb7b99d78937b
|
Loading…
x
Reference in New Issue
Block a user