diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index b8cb97a533..8f46523b15 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -594,7 +594,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $Z = null; Level::getXZ($index, $X, $Z); - if(!$this->level->populateChunk($X, $Z, !$this->spawned or count($this->usedChunks) < ($this->viewDistance - 24))){ + if(!$this->level->populateChunk($X, $Z)){ if($this->spawned){ continue; }else{ diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 9219190504..2fe0408163 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -91,7 +91,6 @@ use pocketmine\plugin\Plugin; use pocketmine\plugin\PluginLoadOrder; use pocketmine\plugin\PluginManager; use pocketmine\scheduler\CallbackTask; -use pocketmine\scheduler\GarbageCollectionTask; use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\ServerScheduler; use pocketmine\tile\Chest; @@ -100,7 +99,6 @@ use pocketmine\tile\Sign; use pocketmine\tile\Tile; use pocketmine\updater\AutoUpdater; use pocketmine\utils\Binary; -use pocketmine\utils\Cache; use pocketmine\utils\Config; use pocketmine\utils\LevelException; use pocketmine\utils\MainLogger; @@ -147,9 +145,6 @@ class Server{ /** @var ServerScheduler */ private $scheduler = null; - /** @var GenerationRequestManager */ - private $generationManager = null; - /** * Counts the ticks since the server start * @@ -569,13 +564,6 @@ class Server{ return $this->scheduler; } - /** - * @return GenerationRequestManager - */ - public function getGenerationManager(){ - return $this->generationManager; - } - /** * @return int */ @@ -1192,7 +1180,7 @@ class Server{ foreach($order as $index => $distance){ Level::getXZ($index, $chunkX, $chunkZ); - $level->generateChunk($chunkX, $chunkZ); + $level->generateChunk($chunkX, $chunkZ, true); } return true; @@ -1624,12 +1612,6 @@ class Server{ $this->enablePlugins(PluginLoadOrder::STARTUP); - if($this->getProperty("chunk-generation.use-async", true)){ - $this->generationManager = new GenerationRequestManager($this); - }else{ - $this->generationManager = new GenerationInstanceManager($this); - } - LevelProviderManager::addProvider($this, Anvil::class); LevelProviderManager::addProvider($this, McRegion::class); if(extension_loaded("leveldb")){ @@ -1692,8 +1674,6 @@ class Server{ $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([$this, "doLevelGC"]), $this->getProperty("chunk-gc.period-in-ticks", 600), $this->getProperty("chunk-gc.period-in-ticks", 600)); } - $this->scheduler->scheduleRepeatingTask(new GarbageCollectionTask(), 900); - $this->enablePlugins(PluginLoadOrder::POSTWORLD); $this->start(); @@ -1967,10 +1947,6 @@ class Server{ $this->unloadLevel($level, true); } - if($this->generationManager instanceof GenerationRequestManager){ - $this->generationManager->shutdown(); - } - HandlerList::unregisterAll(); $this->scheduler->cancelAllTasks(); @@ -2368,16 +2344,6 @@ class Server{ } } - Timings::$generationTimer->startTiming(); - try{ - $this->generationManager->process(); - }catch(\Exception $e){ - if($this->logger instanceof MainLogger){ - $this->logger->logException($e); - } - } - Timings::$generationTimer->stopTiming(); - if(($this->tickCounter % 100) === 0){ foreach($this->levels as $level){ $level->clearCache(); diff --git a/src/pocketmine/level/ChunkManager.php b/src/pocketmine/level/ChunkManager.php index 0de681802b..124f259e5a 100644 --- a/src/pocketmine/level/ChunkManager.php +++ b/src/pocketmine/level/ChunkManager.php @@ -79,7 +79,7 @@ interface ChunkManager{ * @param int $chunkZ * @param FullChunk $chunk */ - public function setChunk($chunkX, $chunkZ, FullChunk $chunk); + public function setChunk($chunkX, $chunkZ, FullChunk $chunk = null); /** * Gets the level seed diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index df44254830..a97a1819d2 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -66,6 +66,7 @@ use pocketmine\level\format\FullChunk; use pocketmine\level\format\generic\BaseLevelProvider; use pocketmine\level\format\generic\EmptyChunkSection; use pocketmine\level\format\LevelProvider; +use pocketmine\level\generator\GenerationTask; use pocketmine\level\generator\Generator; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Math; @@ -173,6 +174,7 @@ class Level implements ChunkManager, Metadatable{ private $chunkSendTasks = []; private $chunkGenerationQueue = []; + private $chunkGenerationQueueSize = 16; private $autoSave = true; @@ -306,6 +308,7 @@ class Level implements ChunkManager, Metadatable{ $this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4))); $this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 260); $this->chunksPopulatedPerTick = (int) $this->server->getProperty("chunk-generation.populations-per-tick", 1); + $this->chunkGenerationQueueSize = (int) $this->server->getProperty("chunk-generation.queue-size", 16); $this->chunkTickList = []; $this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false); @@ -315,7 +318,6 @@ class Level implements ChunkManager, Metadatable{ } public function initLevel(){ - $this->server->getGenerationManager()->openLevel($this, $this->generator, $this->provider->getGeneratorOptions()); $generator = $this->generator; $this->generatorInstance = new $generator($this->provider->getGeneratorOptions()); $this->generatorInstance->init($this, new Random($this->getSeed())); @@ -361,7 +363,6 @@ class Level implements ChunkManager, Metadatable{ $this->unloadChunk($chunk->getX(), $chunk->getZ(), false); } - $this->server->getGenerationManager()->closeLevel($this); $this->provider->close(); $this->provider = null; $this->blockMetadata = null; @@ -1832,7 +1833,10 @@ class Level implements ChunkManager, Metadatable{ Timings::$generationTimer->stopTiming(); } - public function setChunk($x, $z, FullChunk $chunk, $unload = true){ + public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){ + if($chunk === null){ + return; + } $index = Level::chunkHash($x, $z); if($unload){ foreach($this->getUsingChunk($x, $z) as $player){ @@ -2313,14 +2317,14 @@ class Level implements ChunkManager, Metadatable{ } public function generateChunk($x, $z, $force = false){ + if(count($this->chunkGenerationQueue) >= $this->chunkGenerationQueueSize and !$force){ + return; + } + if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){ - $this->chunkGenerationQueue[$index] = 0; - $this->server->getGenerationManager()->requestChunk($this, $x, $z, $this->getChunk($x, $z, true)); - }elseif($force){ - $value = ++$this->chunkGenerationQueue[$index]; - if($value % 40 === 0){ - $this->server->getGenerationManager()->requestChunk($this, $x, $z); - } + $this->chunkGenerationQueue[$index] = true; + $task = new GenerationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true)); + $this->server->getScheduler()->scheduleAsyncTask($task); } } diff --git a/src/pocketmine/level/SimpleChunkManager.php b/src/pocketmine/level/SimpleChunkManager.php new file mode 100644 index 0000000000..d1dab2b340 --- /dev/null +++ b/src/pocketmine/level/SimpleChunkManager.php @@ -0,0 +1,128 @@ +seed = $seed; + } + + /** + * Gets the raw block id. + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int 0-255 + */ + public function getBlockIdAt($x, $y, $z){ + if($chunk = $this->getChunk($x >> 4, $z >> 4)){ + return $chunk->getBlockId($x & 0xf, $y & 0x7f, $z & 0xf); + } + return 0; + } + + /** + * 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){ + if($chunk = $this->getChunk($x >> 4, $z >> 4)){ + $chunk->setBlockId($x & 0xf, $y & 0x7f, $z & 0xf, $id); + } + } + + /** + * Gets the raw block metadata + * + * @param int $x + * @param int $y + * @param int $z + * + * @return int 0-15 + */ + public function getBlockDataAt($x, $y, $z){ + if($chunk = $this->getChunk($x >> 4, $z >> 4)){ + return $chunk->getBlockData($x & 0xf, $y & 0x7f, $z & 0xf); + } + return 0; + } + + /** + * 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){ + if($chunk = $this->getChunk($x >> 4, $z >> 4)){ + $chunk->getBlockData($x & 0xf, $y & 0x7f, $z & 0xf, $data); + } + } + + /** + * @param int $chunkX + * @param int $chunkZ + * + * @return FullChunk + */ + public function getChunk($chunkX, $chunkZ){ + return isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunks[$index] : null; + } + + /** + * @param int $chunkX + * @param int $chunkZ + * @param FullChunk $chunk + */ + public function setChunk($chunkX, $chunkZ, FullChunk $chunk = null){ + if($chunk === null){ + unset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]); + return; + } + $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; + } + + /** + * Gets the level seed + * + * @return int + */ + public function getSeed(){ + return $this->seed; + } +} \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationChunkManager.php b/src/pocketmine/level/generator/GenerationChunkManager.php deleted file mode 100644 index fd0f5e61bc..0000000000 --- a/src/pocketmine/level/generator/GenerationChunkManager.php +++ /dev/null @@ -1,238 +0,0 @@ -levelID = $levelID; - $this->seed = $seed; - $this->manager = $manager; - $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 - * - * @throws ChunkException - */ - public function getChunk($chunkX, $chunkZ){ - $index = Level::chunkHash($chunkX, $chunkZ); - $chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index]; - if($chunk === null){ - throw new ChunkException("null Chunk received"); - } - - return $chunk; - } - - public function cleanChangedChunk($index){ - unset($this->chunks[$index]); - } - - public function generateChunk($chunkX, $chunkZ){ - try{ - $this->getChunk($chunkX, $chunkZ); - $this->generator->generateChunk($chunkX, $chunkZ); - $this->setChunkGenerated($chunkX, $chunkZ); - }catch(\Exception $e){ - } - } - - 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){ - try{ - return $this->getChunk($chunkX, $chunkZ)->isGenerated(); - }catch(\Exception $e){ - return false; - } - } - - public function isChunkPopulated($chunkX, $chunkZ){ - try{ - return $this->getChunk($chunkX, $chunkZ)->isPopulated(); - }catch(\Exception $e){ - return false; - } - } - - public function setChunkGenerated($chunkX, $chunkZ){ - try{ - $chunk = $this->getChunk($chunkX, $chunkZ); - $chunk->setGenerated(true); - }catch(\Exception $e){ - } - } - - public function setChunkPopulated($chunkX, $chunkZ){ - try{ - $chunk = $this->getChunk($chunkX, $chunkZ); - $chunk->setPopulated(true); - }catch(\Exception $e){ - } - } - - protected function requestChunk($chunkX, $chunkZ){ - $chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ); - $this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk; - - return $chunk; - } - - /** - * @param int $chunkX - * @param int $chunkZ - * @param FullChunk $chunk - */ - public function setChunk($chunkX, $chunkZ, FullChunk $chunk){ - $this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk; - } - - /** - * Gets the raw block id. - * - * @param int $x - * @param int $y - * @param int $z - * - * @return int 0-255 - */ - public function getBlockIdAt($x, $y, $z){ - try{ - return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f); - }catch(\Exception $e){ - return 0; - } - } - - /** - * 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){ - try{ - $this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff); - }catch(\Exception $e){ - } - } - - /** - * Gets the raw block metadata - * - * @param int $x - * @param int $y - * @param int $z - * - * @return int 0-15 - */ - public function getBlockDataAt($x, $y, $z){ - try{ - return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f); - }catch(\Exception $e){ - return 0; - } - } - - /** - * 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){ - try{ - $this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f); - }catch(\Exception $e){ - } - } - - public function shutdown(){ - foreach($this->chunks as $chunk){ - //TODO: send generated chunks to be saved - } - } - - -} \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationInstanceManager.php b/src/pocketmine/level/generator/GenerationInstanceManager.php deleted file mode 100644 index e4cb58f0dc..0000000000 --- a/src/pocketmine/level/generator/GenerationInstanceManager.php +++ /dev/null @@ -1,99 +0,0 @@ -server = $server; - $this->generationManager = new GenerationLevelManager($this->server, $this); - } - - public function process(){ - $this->generationManager->process(); - } - - public function shutdown(){ - $this->generationManager->shutdown(); - } - - /** - * @param Level $level - * @param string $generator - * @param array $options - */ - public function openLevel(Level $level, $generator, array $options = []){ - $this->generationManager->openLevel($level->getId(), $level->getSeed(), $generator, $options); - } - - /** - * @param Level $level - */ - public function closeLevel(Level $level){ - $this->generationManager->closeLevel($level->getId()); - } - - public function addNamespace($namespace, $path){ - - } - - public function requestChunk(Level $level, $chunkX, $chunkZ){ - $this->generationManager->enqueueChunk($level->getId(), $chunkX, $chunkZ); - } - - public function getChunk($levelID, $chunkX, $chunkZ){ - if(($level = $this->server->getLevel($levelID)) instanceof Level){ - $chunk = $level->getChunk($chunkX, $chunkZ, true); - if($chunk instanceof FullChunk){ - return $chunk; - }else{ - throw new ChunkException("Invalid Chunk given"); - } - }else{ - $this->generationManager->closeLevel($levelID); - return null; - } - } - - public function receiveChunk($levelID, FullChunk $chunk){ - if(($level = $this->server->getLevel($levelID)) instanceof Level){ - $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); - }else{ - $this->generationManager->closeLevel($levelID); - } - } - - -} \ No newline at end of file diff --git a/src/pocketmine/level/generator/GenerationLevelManager.php b/src/pocketmine/level/generator/GenerationLevelManager.php deleted file mode 100644 index 41116b9f5c..0000000000 --- a/src/pocketmine/level/generator/GenerationLevelManager.php +++ /dev/null @@ -1,161 +0,0 @@ -server = $server; - $this->manager = $manager; - $this->maxCount = $this->server->getProperty("chunk-generation.per-tick", 1); - - if($this->maxCount < 1){ - $this->splitCount = $this->maxCount; - $this->maxCount = 1; - }else{ - $this->splitCount = 1; - } - - $this->count = 0; - } - - public function openLevel($levelID, $seed, $class, array $options){ - if(!isset($this->levels[$levelID])){ - $this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options); - } - } - - public function generateChunk($levelID, $chunkX, $chunkZ){ - if(isset($this->levels[$levelID])){ - $this->levels[$levelID]->generateChunk($chunkX, $chunkZ); //Request population directly - if(isset($this->levels[$levelID])){ - $this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ)); - $this->levels[$levelID]->cleanChangedChunk(Level::chunkHash($chunkX, $chunkZ)); - } - } - } - - public function process(){ - if(count($this->requestQueue) > 0){ - if($this->splitCount < 1){ - $this->count += $this->splitCount; - if($this->count < 1){ - return; - }else{ - $this->count = 0; - } - } - - $count = 0; - foreach($this->requestQueue as $levelID => $chunks){ - if($count >= $this->maxCount){ - break; - } - - if(count($chunks) === 0){ - unset($this->requestQueue[$levelID]); - }else{ - $key = key($chunks); - Level::getXZ($key, $chunkX, $chunkZ); - unset($this->requestQueue[$levelID][$key]); - $this->generateChunk($levelID, $chunkX, $chunkZ); - ++$count; - } - } - } - } - - public function shutdown(){ - foreach($this->levels as $level){ - $level->shutdown(); - } - $this->levels = []; - } - - public function closeLevel($levelID){ - if(isset($this->levels[$levelID])){ - $this->levels[$levelID]->shutdown(); - unset($this->levels[$levelID]); - } - } - - public function enqueueChunk($levelID, $chunkX, $chunkZ){ - if(!isset($this->requestQueue[$levelID])){ - $this->requestQueue[$levelID] = []; - } - if(!isset($this->requestQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){ - $this->requestQueue[$levelID][$index] = 1; - }else{ - $this->requestQueue[$levelID][$index]++; - arsort($this->requestQueue[$levelID]); - } - } - - /** - * @param $levelID - * @param $chunkX - * @param $chunkZ - * - * @return FullChunk - */ - public function requestChunk($levelID, $chunkX, $chunkZ){ - return $this->manager->getChunk($levelID, $chunkX, $chunkZ); - } - - public function sendChunk($levelID, FullChunk $chunk){ - $this->manager->receiveChunk($levelID, $chunk); - } - - /** - * @return \Logger - */ - public function getLogger(){ - return $this->server->getLogger(); - } - -} diff --git a/src/pocketmine/level/generator/GenerationManager.php b/src/pocketmine/level/generator/GenerationManager.php deleted file mode 100644 index e8eb66a9ea..0000000000 --- a/src/pocketmine/level/generator/GenerationManager.php +++ /dev/null @@ -1,272 +0,0 @@ -Thread, request chunk generation - * If Thread->Server, request chunk contents / loading - * byte[] payload: - * int32 levelID - * int32 chunkX - * int32 chunkZ - */ - const PACKET_REQUEST_CHUNK = 0x00; - - /* - * Direction: Both - * byte[] payload: - * int32 levelID - * int32 chunkX - * int32 chunkZ - * byte className length - * byte[] className - * byte[] chunk (none if generated flag is not set) - */ - const PACKET_SEND_CHUNK = 0x01; - - /* - * Direction: Server->Thread - * byte[] payload: - * int32 levelID - * int32 seed - * string class that extends pocketmine\level\generator\Noise - * byte[] serialized options array - */ - const PACKET_OPEN_LEVEL = 0x02; - - /* - * Direction: Server->Thread - * byte[] payload: - * int32 levelID - */ - const PACKET_CLOSE_LEVEL = 0x03; - - /* - * Direction: Server->Thread - * no payload - */ - const PACKET_SHUTDOWN = 0xff; - - /** @var GenerationThread */ - protected $thread; - - /** @var \Logger */ - protected $logger; - /** @var \ClassLoader */ - protected $loader; - - /** @var GenerationChunkManager[] */ - protected $levels = []; - - /** @var array */ - protected $requestQueue = []; - - /** @var array */ - protected $needsChunk = []; - - protected $shutdown = false; - - /** - * @param GenerationThread $thread - * @param \Logger $logger - * @param \ClassLoader $loader - */ - public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader){ - $this->thread = $thread; - $this->logger = $logger; - $this->loader = $loader; - $chunkX = $chunkZ = null; - Block::init(); - Biome::init(); - - while($this->shutdown !== true){ - try{ - if(count($this->requestQueue) > 0){ - foreach($this->requestQueue as $levelID => $chunks){ - if(count($chunks) === 0){ - unset($this->requestQueue[$levelID]); - }else{ - $key = key($chunks); - Level::getXZ($key, $chunkX, $chunkZ); - unset($this->requestQueue[$levelID][$key]); - $this->generateChunk($levelID, $chunkX, $chunkZ); - } - } - }else{ - $this->readPacket(); - } - }catch(\Exception $e){ - $this->logger->warning("[Generation Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine()); - } - } - } - - protected function openLevel($levelID, $seed, $class, array $options){ - if(!isset($this->levels[$levelID])){ - $this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options); - } - } - - protected function generateChunk($levelID, $chunkX, $chunkZ){ - if(isset($this->levels[$levelID])){ - $this->levels[$levelID]->generateChunk($chunkX, $chunkZ); - if(isset($this->levels[$levelID])){ - $this->sendChunk($levelID, $this->levels[$levelID]->getChunk($chunkX, $chunkZ)); - $this->levels[$levelID]->cleanChangedChunk(Level::chunkHash($chunkX, $chunkZ)); - } - } - } - - protected function closeLevel($levelID){ - if(isset($this->levels[$levelID])){ - $this->levels[$levelID]->shutdown(); - unset($this->levels[$levelID]); - } - } - - protected function enqueueChunk($levelID, $chunkX, $chunkZ){ - if(!isset($this->requestQueue[$levelID])){ - $this->requestQueue[$levelID] = []; - } - if(!isset($this->requestQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){ - $this->requestQueue[$levelID][$index] = 1; - }else{ - $this->requestQueue[$levelID][$index]++; - arsort($this->requestQueue[$levelID]); - } - } - - protected function receiveChunk($levelID, FullChunk $chunk){ - if($this->needsChunk[$levelID] !== null){ - if($this->needsChunk[$levelID][0] === $chunk->getX() and $this->needsChunk[$levelID][1] === $chunk->getZ()){ - $this->needsChunk[$levelID] = $chunk; - } - }elseif(isset($this->levels[$levelID])){ - $this->levels[$levelID]->setChunk($chunk->getX(), $chunk->getZ(), $chunk); - } - } - - /** - * @param $levelID - * @param $chunkX - * @param $chunkZ - * - * @return FullChunk - */ - public function requestChunk($levelID, $chunkX, $chunkZ){ - $this->needsChunk[$levelID] = [$chunkX, $chunkZ]; - $binary = chr(self::PACKET_REQUEST_CHUNK) . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ); - $this->thread->pushThreadToMainPacket($binary); - - do{ - $this->readPacket(); - }while($this->shutdown !== true and !($this->needsChunk[$levelID] instanceof FullChunk)); - - $chunk = $this->needsChunk[$levelID]; - $this->needsChunk[$levelID] = null; - if($chunk instanceof FullChunk){ - return $chunk; - }else{ - return null; - } - } - - public function sendChunk($levelID, FullChunk $chunk){ - $binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); - $this->thread->pushThreadToMainPacket($binary); - } - - protected function readPacket(){ - if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){ - $pid = ord($packet{0}); - $offset = 1; - if($pid === self::PACKET_REQUEST_CHUNK){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $chunkX = Binary::readInt(substr($packet, $offset, 4)); - $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; - $len = ord($packet{$offset++}); - /** @var FullChunk $class */ - $class = substr($packet, $offset, $len); - $offset += $len; - $chunk = $class::fromBinary(substr($packet, $offset)); - $this->receiveChunk($levelID, $chunk); - }elseif($pid === self::PACKET_OPEN_LEVEL){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $seed = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $len = Binary::readShort(substr($packet, $offset, 2)); - $offset += 2; - $class = substr($packet, $offset, $len); - $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); - }elseif($pid === self::PACKET_SHUTDOWN){ - foreach($this->levels as $level){ - $level->shutdown(); - } - $this->levels = []; - - $this->shutdown = true; - } - }elseif(count($this->thread->getInternalQueue()) === 0){ - $this->thread->synchronized(function (){ - $this->thread->wait(50000); - }); - - } - } - - /** - * @return \Logger - */ - public function getLogger(){ - return $this->logger; - } - -} diff --git a/src/pocketmine/level/generator/GenerationRequestManager.php b/src/pocketmine/level/generator/GenerationRequestManager.php deleted file mode 100644 index c73b5990b5..0000000000 --- a/src/pocketmine/level/generator/GenerationRequestManager.php +++ /dev/null @@ -1,146 +0,0 @@ -server = $server; - $this->generationThread = new GenerationThread($server->getLogger(), $server->getLoader()); - } - - /** - * @param Level $level - * @param string $generator - * @param array $options - */ - public function openLevel(Level $level, $generator, array $options = []){ - $buffer = chr(GenerationManager::PACKET_OPEN_LEVEL) . Binary::writeInt($level->getId()) . Binary::writeInt($level->getSeed()) . - Binary::writeShort(strlen($generator)) . $generator . serialize($options); - - $this->generationThread->pushMainToThreadPacket($buffer); - } - - /** - * @param Level $level - */ - public function closeLevel(Level $level){ - $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($level->getId()); - $this->generationThread->pushMainToThreadPacket($buffer); - } - - /** - * @deprecated - */ - public function addNamespace($namespace, $path){ - - } - - protected function sendChunk($levelID, FullChunk $chunk){ - $buffer = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary(); - $this->generationThread->pushMainToThreadPacket($buffer); - } - - public function requestChunk(Level $level, $chunkX, $chunkZ, FullChunk $chunk = null){ - $buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getId()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ); - $this->generationThread->pushMainToThreadPacket($buffer); - if($chunk !== null){ - $this->sendChunk($level->getId(), $chunk); - } - } - - protected function handleRequest($levelID, $chunkX, $chunkZ){ - if(($level = $this->server->getLevel($levelID)) instanceof Level){ - $chunk = $level->getChunk($chunkX, $chunkZ, true); - if($chunk instanceof FullChunk){ - $this->sendChunk($levelID, $chunk); - }else{ - throw new ChunkException("Invalid Chunk given"); - } - }else{ - $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID); - $this->generationThread->pushMainToThreadPacket($buffer); - } - } - - protected function receiveChunk($levelID, FullChunk $chunk){ - if(($level = $this->server->getLevel($levelID)) instanceof Level){ - $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); - }else{ - $buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID); - $this->generationThread->pushMainToThreadPacket($buffer); - } - } - - public function process(){ - $this->handlePackets(); - } - - public function handlePackets(){ - while(strlen($packet = $this->generationThread->readThreadToMainPacket()) > 0){ - $pid = ord($packet{0}); - $offset = 1; - - if($pid === GenerationManager::PACKET_REQUEST_CHUNK){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $chunkX = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $chunkZ = Binary::readInt(substr($packet, $offset, 4)); - $this->handleRequest($levelID, $chunkX, $chunkZ); - }elseif($pid === GenerationManager::PACKET_SEND_CHUNK){ - $levelID = Binary::readInt(substr($packet, $offset, 4)); - $offset += 4; - $len = ord($packet{$offset++}); - /** @var FullChunk $class */ - $class = substr($packet, $offset, $len); - $offset += $len; - $level = $this->server->getLevel($levelID); - if($level instanceof Level){ - $chunk = $class::fromBinary(substr($packet, $offset), $level->getProvider()); - $this->receiveChunk($levelID, $chunk); - } - } - } - } - - public function shutdown(){ - $buffer = chr(GenerationManager::PACKET_SHUTDOWN); - $this->generationThread->pushMainToThreadPacket($buffer); - } - - -} diff --git a/src/pocketmine/level/generator/GenerationTask.php b/src/pocketmine/level/generator/GenerationTask.php new file mode 100644 index 0000000000..0d7e2dff90 --- /dev/null +++ b/src/pocketmine/level/generator/GenerationTask.php @@ -0,0 +1,100 @@ +generator = get_class($generator); + $this->settings = $generator->getSettings(); + $this->seed = $level->getSeed(); + $this->levelId = $level->getId(); + $this->chunk = $chunk->toBinary(); + $this->chunkClass = get_class($chunk); + } + + public function onRun(){ + /** @var SimpleChunkManager $manager */ + $manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager"); + /** @var Generator $generator */ + $generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator"); + if($manager === null or $generator === null){ + Block::init(); + Biome::init(); + $manager = new SimpleChunkManager($this->seed); + $this->saveToThreadStore($key, $manager); + /** @var Generator $generator */ + $generator = $this->generator; + $generator = new $generator($this->settings); + $generator->init($manager, new Random($manager->getSeed())); + $this->saveToThreadStore($gKey, $generator); + } + + /** @var FullChunk $chunk */ + $chunk = $this->chunkClass; + $chunk = $chunk::fromBinary($this->chunk); + if($chunk === null){ + //TODO error + return; + } + + $manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk); + + $generator->generateChunk($chunk->getX(), $chunk->getZ()); + + $chunk = $manager->getChunk($chunk->getX(), $chunk->getZ()); + $chunk->setGenerated(true); + $this->chunk = $chunk->toBinary(); + + $manager->setChunk($chunk->getX(), $chunk->getZ(), null); + } + + public function onCompletion(Server $server){ + $level = $server->getLevel($this->levelId); + if($level !== null){ + /** @var FullChunk $chunk */ + $chunk = $this->chunkClass; + $chunk = $chunk::fromBinary($this->chunk); + if($chunk === null){ + //TODO error + return; + } + $level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk); + } + } +} diff --git a/src/pocketmine/level/generator/GenerationThread.php b/src/pocketmine/level/generator/GenerationThread.php deleted file mode 100644 index c7241d709f..0000000000 --- a/src/pocketmine/level/generator/GenerationThread.php +++ /dev/null @@ -1,120 +0,0 @@ -internalQueue; - } - - /** - * @return \Threaded - */ - public function getExternalQueue(){ - return $this->externalQueue; - } - - public function pushMainToThreadPacket($str){ - $this->internalQueue[] = $str; - $this->synchronized(function (){ - $this->notify(); - }); - } - - public function readMainToThreadPacket(){ - return $this->internalQueue->shift(); - } - - public function pushThreadToMainPacket($str){ - $this->externalQueue[] = $str; - } - - public function readThreadToMainPacket(){ - return $this->externalQueue->shift(); - } - - /** - * @return \ThreadedLogger - */ - public function getLogger(){ - return $this->logger; - } - - public function __construct(\ThreadedLogger $logger, \ClassLoader $loader){ - $this->loader = $loader; - $this->logger = $logger; - $loadPaths = []; - $this->addDependency($loadPaths, new \ReflectionClass($this->loader)); - $this->loadPaths = array_reverse($loadPaths); - - $this->externalQueue = \ThreadedFactory::create(); - $this->internalQueue = \ThreadedFactory::create(); - - $this->start(); - } - - protected function addDependency(array &$loadPaths, \ReflectionClass $dep){ - if($dep->getFileName() !== false){ - $loadPaths[$dep->getName()] = $dep->getFileName(); - } - - if($dep->getParentClass() instanceof \ReflectionClass){ - $this->addDependency($loadPaths, $dep->getParentClass()); - } - - foreach($dep->getInterfaces() as $interface){ - $this->addDependency($loadPaths, $interface); - } - } - - public function run(){ - error_reporting(-1); - gc_enable(); - //Load removed dependencies, can't use require_once() - foreach($this->loadPaths as $name => $path){ - if(!class_exists($name, false) and !interface_exists($name, false)){ - require($path); - } - } - $this->loader->register(); - - $generationManager = new GenerationManager($this, $this->getLogger(), $this->loader); - } -} diff --git a/src/pocketmine/network/RakLibInterface.php b/src/pocketmine/network/RakLibInterface.php index 99354219a3..142fe965ba 100644 --- a/src/pocketmine/network/RakLibInterface.php +++ b/src/pocketmine/network/RakLibInterface.php @@ -168,7 +168,7 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{ public function close(Player $player, $reason = "unknown reason"){ if(isset($this->identifiers[$player])){ unset($this->players[$this->identifiers[$player]]); - unset($this->batchedPackets[$identifier]); + unset($this->batchedPackets[$this->identifiers[$player]]); unset($this->identifiersACK[$this->identifiers[$player]]); $this->interface->closeSession($this->identifiers[$player], $reason); $this->identifiers->detach($player); diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 5d8928d81e..42324ebcc3 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -14,7 +14,7 @@ settings: #Sends anonymous statistics to create usage reports send-usage: true #Number of AsyncTask workers. - #Used for plugin asynchronous tasks, compression and web communication. + #Used for plugin asynchronous tasks, world generation, compression and web communication. async-workers: 2 network: @@ -59,12 +59,9 @@ chunk-ticking: clear-tick-list: false chunk-generation: - #Whether to run the generation on a different thread or on the main thread - #Generation will be less glitchy on the main thread, but will lag more - #Using this with fast generators is recommended - use-async: true - #Max. amount of chunks to generate per tick, only for use-async: false - per-tick: 3 + #Max. amount of chunks in the waiting queue ot be generated + #It's recommended to set this to 8 * settings.async-workers + queue-size: 16 #Max. amount of chunks to populate per tick populations-per-tick: 1 diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php index cb577f7990..39f7142037 100644 --- a/src/pocketmine/scheduler/AsyncPool.php +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -14,7 +14,7 @@ * (at your option) any later version. * * @author PocketMine Team - + * @link http://www.pocketmine.net/ * * */ @@ -51,6 +51,10 @@ class AsyncPool{ } } + public function getSize(){ + return $this->size; + } + public function submitTask(AsyncTask $task){ if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){ return; diff --git a/src/pocketmine/scheduler/AsyncTask.php b/src/pocketmine/scheduler/AsyncTask.php index 5297395c39..e9ca81542c 100644 --- a/src/pocketmine/scheduler/AsyncTask.php +++ b/src/pocketmine/scheduler/AsyncTask.php @@ -30,6 +30,9 @@ use pocketmine\Server; */ abstract class AsyncTask extends \Collectable{ + /** @var AsyncWorker $worker */ + public $worker = null; + private $result = null; private $serialized = false; /** @var int */ @@ -83,6 +86,32 @@ abstract class AsyncTask extends \Collectable{ return $this->taskId; } + /** + * Gets something into the local thread store. + * You have to initialize this in some way from the task on run + * + * @param string $identifier + * @return mixed + */ + public function getFromThreadStore($identifier){ + global $store; + return $this->isGarbage() ? null : $store[$identifier]; + } + + /** + * Saves something into the local thread store. + * This might get deleted at any moment. + * + * @param string $identifier + * @param mixed $value + */ + public function saveToThreadStore($identifier, $value){ + global $store; + if(!$this->isGarbage()){ + $store[$identifier] = $value; + } + } + /** * Actions to execute when run * diff --git a/src/pocketmine/scheduler/AsyncWorker.php b/src/pocketmine/scheduler/AsyncWorker.php index 95c3716f27..3b78e1eb0d 100644 --- a/src/pocketmine/scheduler/AsyncWorker.php +++ b/src/pocketmine/scheduler/AsyncWorker.php @@ -38,6 +38,9 @@ class AsyncWorker extends Worker{ require(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php"); } $this->loader->register(true); + + global $store; + $store = []; } public function start($options = PTHREADS_INHERIT_NONE){ diff --git a/src/pocketmine/scheduler/GarbageCollectionTask.php b/src/pocketmine/scheduler/GarbageCollectionTask.php deleted file mode 100644 index f6b3e2d632..0000000000 --- a/src/pocketmine/scheduler/GarbageCollectionTask.php +++ /dev/null @@ -1,32 +0,0 @@ -