*/ protected $unloadQueue; /** @var Generator */ protected $generator; /** @var GenerationManager */ protected $manager; 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"); } $this->levelID = $levelID; $this->seed = $seed; $this->manager = $manager; $this->unloadQueue = new \SplObjectStorage(); $this->generator = new $class($options); $this->generator->init($this, new Random($seed)); } /** * @return int */ public function getSeed(){ return $this->seed; } /** * @return int */ public function getID(){ return $this->levelID; } /** * @param $chunkX * @param $chunkZ * * @return FullChunk */ public function getChunk($chunkX, $chunkZ){ $index = Level::chunkHash($chunkX, $chunkZ); $chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index]; $this->unloadQueue->detach($chunk); $this->changes[$index] = $chunk; return $chunk; } /** * @return FullChunk[] */ public function getChangedChunks(){ return $this->changes; } public function cleanChangedChunks(){ $this->changes = []; } public function doGarbageCollection(){ if($this->unloadQueue->count() > 0){ /** @var FullChunk $chunk */ foreach($this->unloadQueue as $chunk){ if(isset($this->changes[$index = Level::chunkHash($chunk->getX(), $chunk->getZ())])){ continue; } unset($this->chunks[$index]); $this->unloadQueue->detach($chunk); } } foreach($this->chunks as $chunk){ if(isset($this->changes[$index = Level::chunkHash($chunk->getX(), $chunk->getZ())])){ continue; } $this->unloadQueue->attach($chunk); } } public function generateChunk($chunkX, $chunkZ){ $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $this->requestChunk($chunkX, $chunkZ); $this->generator->generateChunk($chunkX, $chunkZ); $this->setChunkGenerated($chunkX, $chunkZ); } public function populateChunk($chunkX, $chunkZ){ if(!$this->isChunkGenerated($chunkX, $chunkZ)){ $this->generateChunk($chunkX, $chunkZ); } for($z = $chunkZ - 1; $z <= $chunkZ + 1; ++$z){ for($x = $chunkX - 1; $x <= $chunkX + 1; ++$x){ if(!$this->isChunkGenerated($x, $z)){ $this->generateChunk($x, $z); } } } $this->generator->populateChunk($chunkX, $chunkZ); $this->setChunkPopulated($chunkX, $chunkZ); } public function isChunkGenerated($chunkX, $chunkZ){ return $this->getChunk($chunkX, $chunkZ)->isGenerated(); } public function isChunkPopulated($chunkX, $chunkZ){ return $this->getChunk($chunkX, $chunkZ)->isPopulated(); } public function setChunkGenerated($chunkX, $chunkZ){ $chunk = $this->getChunk($chunkX, $chunkZ); $chunk->setGenerated(true); } public function setChunkPopulated($chunkX, $chunkZ){ $chunk = $this->getChunk($chunkX, $chunkZ); $chunk->setPopulated(true); } protected function requestChunk($chunkX, $chunkZ){ $chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ); $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; return $this->chunks[$index]; } /** * @param int $chunkX * @param int $chunkZ * @param FullChunk $chunk */ 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 } } /** * Gets the raw block id. * * @param int $x * @param int $y * @param int $z * * @return int 0-255 */ public function getBlockIdAt($x, $y, $z){ return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); } /** * Sets the raw block id. * * @param int $x * @param int $y * @param int $z * @param int $id 0-255 */ public function setBlockIdAt($x, $y, $z, $id){ $this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); } /** * Gets the raw block metadata * * @param int $x * @param int $y * @param int $z * * @return int 0-15 */ public function getBlockDataAt($x, $y, $z){ return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); } /** * Sets the raw block metadata. * * @param int $x * @param int $y * @param int $z * @param int $data 0-15 */ public function setBlockDataAt($x, $y, $z, $data){ $this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); } public function shutdown(){ foreach($this->chunks as $chunk){ //TODO: send generated chunks to be saved } } }