diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 4498173b2..cdaa49061 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1465,22 +1465,6 @@ class Server{ $this->dataPath = realpath($dataPath) . DIRECTORY_SEPARATOR; $this->pluginPath = realpath($pluginPath) . DIRECTORY_SEPARATOR; - $this->entityMetadata = new EntityMetadataStore(); - $this->playerMetadata = new PlayerMetadataStore(); - $this->levelMetadata = new LevelMetadataStore(); - - $this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM); - $this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM); - if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){ - @rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt"); - } - @touch($this->dataPath . "banned-players.txt"); - $this->banByName = new BanList($this->dataPath . "banned-players.txt"); - $this->banByName->load(); - @touch($this->dataPath . "banned-ips.txt"); - $this->banByIP = new BanList($this->dataPath . "banned-ips.txt"); - $this->banByIP->load(); - $this->consoleThreaded = new \Threaded(); $this->console = new CommandReader($this->consoleThreaded); @@ -1532,6 +1516,22 @@ class Server{ $this->rcon = new RCON($this, $this->getConfigString("rcon.password", ""), $this->getConfigInt("rcon.port", $this->getPort()), ($ip = $this->getIp()) != "" ? $ip : "0.0.0.0", $this->getConfigInt("rcon.threads", 1), $this->getConfigInt("rcon.clients-per-thread", 50)); } + $this->entityMetadata = new EntityMetadataStore(); + $this->playerMetadata = new PlayerMetadataStore(); + $this->levelMetadata = new LevelMetadataStore(); + + $this->operators = new Config($this->dataPath . "ops.txt", Config::ENUM); + $this->whitelist = new Config($this->dataPath . "white-list.txt", Config::ENUM); + if(file_exists($this->dataPath . "banned.txt") and !file_exists($this->dataPath . "banned-players.txt")){ + @rename($this->dataPath . "banned.txt", $this->dataPath . "banned-players.txt"); + } + @touch($this->dataPath . "banned-players.txt"); + $this->banByName = new BanList($this->dataPath . "banned-players.txt"); + $this->banByName->load(); + @touch($this->dataPath . "banned-ips.txt"); + $this->banByIP = new BanList($this->dataPath . "banned-ips.txt"); + $this->banByIP->load(); + $this->maxPlayers = $this->getConfigInt("max-players", 20); $this->setAutoSave($this->getConfigBoolean("auto-save", true)); @@ -2134,7 +2134,7 @@ class Server{ public function sendUsage(){ if($this->lastSendUsage instanceof SendUsageTask){ - if(!$this->lastSendUsage->isFinished()){ //do not call multiple times + if(!$this->lastSendUsage->isGarbage()){ //do not call multiple times return; } } @@ -2146,7 +2146,7 @@ class Server{ } $version = new VersionString(); - $this->lastSendUsage = new SendUsageTask("http://stats.pocketmine.net/usage.php", [ + $this->lastSendUsage = new SendUsageTask("https://stats.pocketmine.net/usage.php", [ "serverid" => $this->serverID, "port" => $this->getPort(), "os" => Utils::getOS(), diff --git a/src/pocketmine/Thread.php b/src/pocketmine/Thread.php index 6a06a0b66..c6b83d925 100644 --- a/src/pocketmine/Thread.php +++ b/src/pocketmine/Thread.php @@ -26,9 +26,13 @@ namespace pocketmine; */ abstract class Thread extends \Thread{ - public final function start($options = PTHREADS_INHERIT_ALL){ + public function start($options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); - return parent::start($options); + if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){ + return parent::start($options); + } + + return false; } } \ No newline at end of file diff --git a/src/pocketmine/Worker.php b/src/pocketmine/Worker.php index b6b8e890a..7f65b6e0d 100644 --- a/src/pocketmine/Worker.php +++ b/src/pocketmine/Worker.php @@ -26,9 +26,13 @@ namespace pocketmine; */ abstract class Worker extends \Worker{ - public final function start($options = PTHREADS_INHERIT_ALL){ + public function start($options = PTHREADS_INHERIT_ALL){ ThreadManager::getInstance()->add($this); - return parent::start($options); + if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated() and !$this->isShutdown()){ + return parent::start($options); + } + + return false; } } \ No newline at end of file diff --git a/src/pocketmine/command/CommandReader.php b/src/pocketmine/command/CommandReader.php index 076f868fe..076f14166 100644 --- a/src/pocketmine/command/CommandReader.php +++ b/src/pocketmine/command/CommandReader.php @@ -34,7 +34,7 @@ class CommandReader extends Thread{ */ public function __construct(\Threaded $threaded){ $this->buffer = $threaded; - $this->start(); + $this->start(PTHREADS_INHERIT_NONE); } private function readLine(){ diff --git a/src/pocketmine/resources/pocketmine.yml b/src/pocketmine/resources/pocketmine.yml index 9c487125a..cee9d001c 100644 --- a/src/pocketmine/resources/pocketmine.yml +++ b/src/pocketmine/resources/pocketmine.yml @@ -15,7 +15,8 @@ settings: #Sends anonymous statistics to create usage reports send-usage: true #Number of AsyncTask workers - async-workers: 4 + #WARNING: This will increase global memory usage, but it won't be listed in the total. + async-workers: 1 debug: #If > 1, it will show debug messages in the console @@ -27,6 +28,7 @@ level-settings: #The default format that levels will use when created default-format: mcregion #If true, converts from a format that is not the default to the default format on load + #NOTE: This is currently not implemented convert-format: false chunk-sending: @@ -42,6 +44,7 @@ chunk-ticking: per-tick: 260 #Radius of chunks around a player to tick tick-radius: 4 + #NOTE: This is currently not implemented light-updates: false clear-tick-list: false diff --git a/src/pocketmine/scheduler/AsyncPool.php b/src/pocketmine/scheduler/AsyncPool.php new file mode 100644 index 000000000..15987d5ce --- /dev/null +++ b/src/pocketmine/scheduler/AsyncPool.php @@ -0,0 +1,109 @@ +server = $server; + $this->size = (int) $size; + + for($i = 0; $i < $this->size; ++$i){ + $this->workerUsage[$i] = 0; + $this->workers[$i] = new AsyncWorker(); + $this->workers[$i]->start(); + } + } + + public function submitTask(AsyncTask $task){ + if(isset($this->tasks[$task->getTaskId()]) or $task->isGarbage()){ + return; + } + + $this->tasks[$task->getTaskId()] = $task; + + $selectedWorker = mt_rand(0, $this->size - 1); + $selectedTasks = $this->workerUsage[$selectedWorker]; + for($i = 0; $i < $this->size; ++$i){ + if($this->workerUsage[$i] < $selectedTasks){ + $selectedWorker = $i; + $selectedTasks = $this->workerUsage[$i]; + } + } + + $this->workers[$selectedWorker]->stack($task); + $this->workerUsage[$selectedWorker]++; + $this->taskWorkers[$task->getTaskId()] = $selectedWorker; + } + + private function removeTask(AsyncTask $task){ + if(isset($this->taskWorkers[$task->getTaskId()])){ + $this->workers[$this->taskWorkers[$task->getTaskId()]]->unstack($task); + } + + unset($this->tasks[$task->getTaskId()]); + unset($this->taskWorkers[$task->getTaskId()]); + } + + public function removeTasks(){ + foreach($this->tasks as $task){ + $this->removeTask($task); + } + + for($i = 0; $i < $this->size; ++$i){ + $this->workerUsage[$i] = 0; + } + + $this->taskWorkers = []; + $this->tasks = []; + } + + public function collectTasks(){ + foreach($this->tasks as $task){ + if($task->isGarbage()){ + + $task->onCompletion($this->server); + + $this->workerUsage[$this->taskWorkers[$task->getTaskId()]]--; + unset($this->tasks[$task->getTaskId()]); + unset($this->taskWorkers[$task->getTaskId()]); + } + } + } +} \ No newline at end of file diff --git a/src/pocketmine/scheduler/AsyncTask.php b/src/pocketmine/scheduler/AsyncTask.php index 4c6341c4d..c363b712a 100644 --- a/src/pocketmine/scheduler/AsyncTask.php +++ b/src/pocketmine/scheduler/AsyncTask.php @@ -30,23 +30,25 @@ use pocketmine\Server; */ abstract class AsyncTask extends \Collectable{ - private $finished = null; private $result = null; + /** @var int */ private $taskId = null; public function run(){ - $this->finished = false; $this->result = null; $this->onRun(); - $this->finished = true; + + $this->setGarbage(); } /** + * @deprecated + * * @return bool */ public function isFinished(){ - return $this->finished === true; + return $this->isGarbage(); } /** diff --git a/src/pocketmine/scheduler/AsyncWorker.php b/src/pocketmine/scheduler/AsyncWorker.php index 7b8530f9d..0ce882b28 100644 --- a/src/pocketmine/scheduler/AsyncWorker.php +++ b/src/pocketmine/scheduler/AsyncWorker.php @@ -34,6 +34,11 @@ class AsyncWorker extends Worker{ } $autoloader = new \BaseClassLoader(); $autoloader->addPath(\pocketmine\PATH . "src"); + $autoloader->addPath(\pocketmine\PATH . "src" . DIRECTORY_SEPARATOR . "spl"); $autoloader->register(true); } + + public function start($options = PTHREADS_INHERIT_NONE){ + parent::start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_FUNCTIONS); + } } \ No newline at end of file diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php index 0c9b55e49..0eb11e5b2 100644 --- a/src/pocketmine/scheduler/SendUsageTask.php +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -37,9 +37,4 @@ class SendUsageTask extends AsyncTask{ public function onRun(){ Utils::postURL($this->endpoint, unserialize($this->data)); } - - public function onCompletion(Server $server){ - - } - } diff --git a/src/pocketmine/scheduler/ServerScheduler.php b/src/pocketmine/scheduler/ServerScheduler.php index 8e3611ebf..dd712cb7e 100644 --- a/src/pocketmine/scheduler/ServerScheduler.php +++ b/src/pocketmine/scheduler/ServerScheduler.php @@ -42,14 +42,9 @@ class ServerScheduler{ */ protected $tasks = []; - /** @var \Pool */ + /** @var AsyncPool */ protected $asyncPool; - /** @var AsyncTask[] */ - protected $asyncTaskStorage = []; - - protected $asyncTasks = 0; - /** @var int */ private $ids = 1; @@ -58,7 +53,7 @@ class ServerScheduler{ public function __construct(){ $this->queue = new ReversePriorityQueue(); - $this->asyncPool = new \Pool(self::$WORKERS, AsyncWorker::class); + $this->asyncPool = new AsyncPool(Server::getInstance(), self::$WORKERS); } /** @@ -80,9 +75,7 @@ class ServerScheduler{ public function scheduleAsyncTask(AsyncTask $task){ $id = $this->nextId(); $task->setTaskId($id); - $this->asyncPool->submit($task); - $this->asyncTaskStorage[$task->getTaskId()] = $task; - ++$this->asyncTasks; + $this->asyncPool->submitTask($task); } /** @@ -144,11 +137,9 @@ class ServerScheduler{ $task->cancel(); } $this->tasks = []; - $this->asyncTaskStorage = []; - //$this->asyncPool->shutdown(); - $this->asyncTasks = 0; + $this->asyncPool->removeTasks(); $this->queue = new ReversePriorityQueue(); - $this->asyncPool = new \Pool(self::$WORKERS, AsyncWorker::class); + $this->ids = 1; } /** @@ -237,7 +228,8 @@ class ServerScheduler{ $task->run($this->currentTick); }catch(\Exception $e){ Server::getInstance()->getLogger()->critical("Could not execute task " . $task->getTaskName() . ": " . $e->getMessage()); - if(($logger = Server::getInstance()->getLogger()) instanceof MainLogger){ + $logger = Server::getInstance()->getLogger(); + if($logger instanceof MainLogger){ $logger->logException($e); } } @@ -252,26 +244,7 @@ class ServerScheduler{ } } - if($this->asyncTasks > 0){ //Garbage collector - $this->asyncPool->collect([$this, "collectAsyncTask"]); - - if($this->asyncTasks > 0){ - foreach($this->asyncTaskStorage as $asyncTask){ - $this->collectAsyncTask($asyncTask); - } - } - } - } - - public function collectAsyncTask(AsyncTask $task){ - if($task->isFinished() and !$task->isGarbage()){ - --$this->asyncTasks; - $task->onCompletion(Server::getInstance()); - $task->setGarbage(); - unset($this->asyncTaskStorage[$task->getTaskId()]); - } - - return $task->isGarbage(); + $this->asyncPool->collectTasks(); } private function isReady($currentTicks){