diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 5586f5f39..9a42628ae 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -645,7 +645,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->chunksOrder = $newOrder; $i = 0; - while($generateQueue->count() > 0 and $i < 8){ + while(count($this->chunksOrder) === 0 and $generateQueue->count() > 0 and $i < 8){ $d = $generateQueue->extract(); $this->getLevel()->generateChunk($d[0], $d[1]); ++$i; diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index d9ec1dfeb..2b6dc37ff 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -997,8 +997,8 @@ class Server{ $level = new Level($this, $name, $path, $provider); $this->levels[$level->getID()] = $level; - for($Z = 6; $Z <= 10; ++$Z){ - for($X = 6; $X <= 10; ++$X){ + for($Z = 5; $Z <= 11; ++$Z){ + for($X = 5; $X <= 11; ++$X){ $level->generateChunk($X, $Z); } } diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 19e2ba87d..61df47511 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1144,6 +1144,10 @@ class Level implements ChunkManager, Metadatable{ } public function setChunk($x, $z, SimpleChunk $chunk){ + $index = Level::chunkHash($x, $z); + foreach($this->getUsingChunk($x, $z) as $player){ + $player->setChunkIndex($index, 0xff); + } $this->provider->setChunk($x, $z, $chunk); } diff --git a/src/pocketmine/level/format/SimpleChunk.php b/src/pocketmine/level/format/SimpleChunk.php index d818744fa..7467364c1 100644 --- a/src/pocketmine/level/format/SimpleChunk.php +++ b/src/pocketmine/level/format/SimpleChunk.php @@ -40,6 +40,8 @@ class SimpleChunk{ protected $flags = 0; + protected $changed = false; + /** * @param int $chunkX * @param int $chunkZ @@ -57,6 +59,15 @@ class SimpleChunk{ } } + public function hasChanged($set = true){ + if($this->changed === true and $set === true){ + $this->changed = false; + return true; + } + + return $this->changed; + } + /** * @return int */ @@ -103,6 +114,7 @@ class SimpleChunk{ * @param bool $value */ public function setGenerated($value = true){ + $this->changed = true; $this->flags = ($this->flags & ~self::FLAG_GENERATED) | ($value === true ? self::FLAG_GENERATED : 0); } @@ -110,6 +122,7 @@ class SimpleChunk{ * @param bool $value */ public function setPopulated($value = true){ + $this->changed = true; $this->flags = ($this->flags & ~self::FLAG_POPULATED) | ($value === true ? self::FLAG_POPULATED : 0); } @@ -131,6 +144,7 @@ class SimpleChunk{ * @param int $blockId 0-255 */ public function setBlockId($x, $y, $z, $blockId){ + $this->changed = true; @$this->ids[$y >> 4]{(($y & 0x0f) << 8) + ($z << 4) + $x} = chr($blockId); } @@ -157,6 +171,7 @@ class SimpleChunk{ * @param int $data 0-15 */ public function setBlockData($x, $y, $z, $data){ + $this->changed = true; $i = (($y & 0x0f) << 7) + ($z << 3) + ($x >> 1); $old_m = ord($this->meta[$y >> 4]{$i}); if(($x & 1) === 0){ @@ -190,6 +205,7 @@ class SimpleChunk{ * @param string $meta */ public function setSection($y, $ids = null, $meta = null){ + $this->changed = true; if($ids !== null){ $this->ids[$y] = $ids; } diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php index 2e4a348ab..757afc8df 100644 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ b/src/pocketmine/level/generator/GenerationChunkManager.php @@ -33,6 +33,9 @@ class GenerationChunkManager implements ChunkManager{ /** @var SimpleChunk[] */ protected $chunks = []; + /** @var \SplObjectStorage */ + protected $unloadQueue; + /** @var Generator */ protected $generator; @@ -50,6 +53,8 @@ class GenerationChunkManager implements ChunkManager{ $this->seed = $seed; $this->manager = $manager; + $this->unloadQueue = new \SplObjectStorage(); + $this->generator = new $class($options); $this->generator->init($this, new Random($seed)); } @@ -76,8 +81,41 @@ class GenerationChunkManager implements ChunkManager{ */ 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); + return $chunk; + } - return !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index]; + /** + * @param bool $set + * + * @return SimpleChunk[] + */ + public function getChangedChunks($set = true){ + $changed = []; + foreach($this->chunks as $chunk){ + if($chunk->hasChanged($set)){ + $changed[] = $chunk; + } + } + + return $changed; + } + + public function doGarbageCollection(){ + if($this->unloadQueue->count() > 0){ + /** @var SimpleChunk $chunk */ + foreach($this->unloadQueue as $chunk){ + if(!$chunk->hasChanged(false)){ + unset($this->chunks[Level::chunkHash($chunk->getX(), $chunk->getZ())]); + } + $this->unloadQueue->detach($chunk); + } + } + + foreach($this->chunks as $chunk){ + $this->unloadQueue->attach($chunk); + } } public function generateChunk($chunkX, $chunkZ){ @@ -101,7 +139,6 @@ class GenerationChunkManager implements ChunkManager{ $this->generator->populateChunk($chunkX, $chunkZ); $this->setChunkPopulated($chunkX, $chunkZ); - } public function isChunkGenerated($chunkX, $chunkZ){ @@ -123,9 +160,6 @@ class GenerationChunkManager implements ChunkManager{ protected function requestChunk($chunkX, $chunkZ){ $chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ); $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; - if(!$chunk->isGenerated()){ - $this->generateChunk($chunkX, $chunkZ); - } return $this->chunks[$index]; } diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php index 047693996..98e58739d 100644 --- a/src/pocketmine/level/generator/GenerationManager.php +++ b/src/pocketmine/level/generator/GenerationManager.php @@ -22,6 +22,7 @@ namespace pocketmine\level\generator; use pocketmine\level\format\SimpleChunk; +use pocketmine\level\Level; use pocketmine\utils\Binary; class GenerationManager{ @@ -97,6 +98,8 @@ class GenerationManager{ /** @var GenerationChunkManager[] */ protected $levels = []; + protected $generatedQueue = []; + /** @var \SplQueue */ protected $requestQueue; @@ -131,16 +134,23 @@ class GenerationManager{ protected function openLevel($levelID, $seed, $class, array $options){ if(!isset($this->levels[$levelID])){ $this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options); + $this->generatedQueue[$levelID] = []; } } protected function generateChunk($levelID, $chunkX, $chunkZ){ - if(isset($this->levels[$levelID])){ + if(isset($this->levels[$levelID]) and !isset($this->generatedQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){ $this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly if(isset($this->levels[$levelID])){ - $this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ)); + $this->generatedQueue[$levelID][$index] = true; + if(count($this->generatedQueue[$levelID]) > 6){ + $this->levels[$levelID]->doGarbageCollection(); + foreach($this->levels[$levelID]->getChangedChunks(true) as $chunk){ + $this->sendChunk($levelID, $chunk); + } + $this->generatedQueue[$levelID] = []; + } } - //TODO: wait for queue generation (to wait for extra chunk changes) } } @@ -148,6 +158,7 @@ class GenerationManager{ if(!isset($this->levels[$levelID])){ $this->levels[$levelID]->shutdown(); unset($this->levels[$levelID]); + unset($this->generatedQueue[$levelID]); } } @@ -193,13 +204,24 @@ class GenerationManager{ @socket_write($this->socket, Binary::writeInt(strlen($binary)) . $binary); } + protected function socketRead($len){ + $buffer = ""; + while(strlen($buffer) < $len){ + $buffer .= @socket_read($this->socket, $len - strlen($buffer)); + } + + return $buffer; + } + protected function readPacket(){ - $len = @socket_read($this->socket, 4); - if($len === false or $len === ""){ + $len = $this->socketRead(4); + if(($len = Binary::readInt($len)) <= 0){ $this->shutdown = true; + $this->getLogger()->critical("Generation Thread found a stream error, shutting down"); return; } - $packet = socket_read($this->socket, Binary::readInt($len)); + + $packet = $this->socketRead($len); $pid = ord($packet{0}); $offset = 1; if($pid === self::PACKET_REQUEST_CHUNK){ @@ -209,13 +231,11 @@ class GenerationManager{ $offset += 4; $chunkZ = Binary::readInt(substr($packet, $offset, 4)); $this->enqueueChunk($levelID, $chunkX, $chunkZ); - }elseif($pid === self::PACKET_SEND_CHUNK){ $levelID = Binary::readInt(substr($packet, $offset, 4)); $offset += 4; $chunk = SimpleChunk::fromBinary(substr($packet, $offset)); $this->receiveChunk($levelID, $chunk); - }elseif($pid === self::PACKET_OPEN_LEVEL){ $levelID = Binary::readInt(substr($packet, $offset, 4)); $offset += 4; @@ -227,7 +247,6 @@ class GenerationManager{ $offset += $len; $options = unserialize(substr($packet, $offset)); $this->openLevel($levelID, $seed, $class, $options); - }elseif($pid === self::PACKET_CLOSE_LEVEL){ $levelID = Binary::readInt(substr($packet, $offset, 4)); $this->closeLevel($levelID);