diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 7eba72bf4..d5943565d 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -71,6 +71,7 @@ use pocketmine\plugin\PluginManager; use pocketmine\pmf\LevelFormat; use pocketmine\recipes\Crafting; use pocketmine\scheduler\CallbackTask; +use pocketmine\scheduler\SendUsageTask; use pocketmine\scheduler\ServerScheduler; use pocketmine\scheduler\TickScheduler; use pocketmine\tile\Sign; @@ -126,6 +127,9 @@ class Server{ /** @var int */ private $maxPlayers; + /** @var RCON */ + private $rcon; + /** * Counts the ticks since the server start * @@ -144,6 +148,8 @@ class Server{ private $dataPath; private $pluginPath; + private $lastSendUsage = null; + /** @var QueryHandler */ private $queryHandler; @@ -1249,14 +1255,7 @@ class Server{ $this->properties->save(); - //TODO - /*if($this->getProperty("send-usage", true) !== false){ - $this->server->schedule(6000, array($this, "sendUsage"), array(), true); //Send the info after 5 minutes have passed - $this->sendUsage(); - } - if(!defined("NO_THREADS") and $this->getProperty("enable-rcon") === true){ - $this->rcon = new RCON($this->getProperty("rcon.password", ""), $this->getProperty("rcon.port", $this->getProperty("server-port")), ($ip = $this->getProperty("server-ip")) != "" ? $ip : "0.0.0.0", $this->getProperty("rcon.threads", 1), $this->getProperty("rcon.clients-per-thread", 50)); - }*/ + $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask("pocketmine\\utils\\Cache::cleanup"), 20 * 45, 20 * 45); if($this->getConfigBoolean("auto-save", true) === true){ $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "doAutoSave")), 18000, 18000); @@ -1371,6 +1370,11 @@ class Server{ } + if($this->getConfigBoolean("send-usage", true) !== false){ + $this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask(array($this, "sendUsage")), 6000, 6000); + $this->sendUsage(); + } + if($this->getConfigBoolean("upnp-forwarding", false) == true){ console("[INFO] [UPnP] Trying to port forward..."); @@ -1389,7 +1393,7 @@ class Server{ } */ console("[INFO] Default game type: " . self::getGamemodeString($this->getGamemode())); //TODO: string name - //$this->trigger("server.start", microtime(true)); + console('[INFO] Done (' . round(microtime(true) - \pocketmine\START_TIME, 3) . 's)! For help, type "help" or "?"'); if(Utils::getOS() === "win"){ //Workaround less usleep() waste $this->tickProcessorWindows(); @@ -1397,6 +1401,15 @@ class Server{ $this->tickProcessor(); } + if($this->rcon instanceof RCON){ + $this->rcon->stop(); + } + + if($this->getConfigBoolean("upnp-forwarding", false) === true){ + console("[INFO] [UPnP] Removing port forward..."); + UPnP::RemovePortForward($this->getPort()); + } + $this->pluginManager->disablePlugins(); foreach($this->players as $player){ @@ -1570,31 +1583,33 @@ class Server{ } public function sendUsage(){ - //TODO - /*console("[DEBUG] Sending usage data...", true, true, 2); + if($this->lastSendUsage instanceof SendUsageTask){ + if(!$this->lastSendUsage->isFinished()){ //do not call multiple times + return; + } + } $plist = ""; - foreach(Server::getInstance()->getPluginManager()->getPlugins() as $p){ + foreach($this->getPluginManager()->getPlugins() as $p){ $d = $p->getDescription(); $plist .= str_replace(array(";", ":"), "", $d->getName()) . ":" . str_replace(array(";", ":"), "", $d->getVersion()) . ";"; } - $this->asyncOperation(ASYNC_CURL_POST, array( - "url" => "http://stats.pocketmine.net/usage.php", - "data" => array( - "serverid" => $this->server->serverID, - "port" => $this->server->port, - "os" => Utils::getOS(), - "memory_total" => $this->getProperty("memory-limit"), - "memory_usage" => memory_get_usage(true), - "php_version" => PHP_VERSION, - "version" => VERSION, - "mc_version" => MINECRAFT_VERSION, - "protocol" => Info::CURRENT_PROTOCOL, - "online" => count($this->players), - "max" => $this->server->maxClients, - "plugins" => $plist, - ), - ), null);*/ + $this->lastSendUsage = new SendUsageTask("http://stats.pocketmine.net/usage.php", array( + "serverid" => Utils::readLong(substr(Utils::getUniqueID(true, $this->getIp() .":". $this->getPort()), 0, 8)), + "port" => $this->getPort(), + "os" => Utils::getOS(), + "memory_total" => $this->getConfigString("memory-limit"), + "memory_usage" => memory_get_usage(), + "php_version" => \pocketmine\PHP_VERSION, + "version" => \pocketmine\VERSION, + "mc_version" => \pocketmine\MINECRAFT_VERSION, + "protocol" => network\protocol\Info::CURRENT_PROTOCOL, + "online" => count($this->players), + "max" => $this->getMaxPlayers(), + "plugins" => $plist, + )); + + $this->scheduler->scheduleAsyncTask($this->lastSendUsage); } public function titleTick(){ diff --git a/src/pocketmine/scheduler/AsyncTask.php b/src/pocketmine/scheduler/AsyncTask.php new file mode 100644 index 000000000..c9e45a6bd --- /dev/null +++ b/src/pocketmine/scheduler/AsyncTask.php @@ -0,0 +1,91 @@ +synchronized(function(){ + $this->result = null; + $this->onRun(\Thread::getCurrentThread()); + $this->finished = true; + $this->complete = $this->result === null ? true : false; + }); + } + + /** + * @return bool + */ + public function isCompleted(){ + return $this->complete === true; + } + + /** + * @return mixed + */ + public function getResult(){ + return $this->synchronized(function(){ + $this->finished = true; + return @unserialize($this->result); + }); + } + + /** + * @return bool + */ + public function hasResult(){ + return $this->result !== null; + } + + /** + * @param mixed $result + */ + public function setResult($result){ + $this->result = @serialize($result); + } + + /** + * @return bool + */ + public function isFinished(){ + return $this->finished === true; + } + + /** + * Actions to execute when run + * + * @param \Thread $thread + * + * @return void + */ + public abstract function onRun(\Thread $thread); + +} diff --git a/src/pocketmine/scheduler/SendUsageTask.php b/src/pocketmine/scheduler/SendUsageTask.php new file mode 100644 index 000000000..add05293d --- /dev/null +++ b/src/pocketmine/scheduler/SendUsageTask.php @@ -0,0 +1,40 @@ +endpoint = $endpoint; + $this->data = serialize($data); + } + + public function onRun(\Thread $thread){ + Utils::postURL($this->endpoint, unserialize($this->data)); + } + +} diff --git a/src/pocketmine/scheduler/ServerScheduler.php b/src/pocketmine/scheduler/ServerScheduler.php index d74acbbb9..9e620796b 100644 --- a/src/pocketmine/scheduler/ServerScheduler.php +++ b/src/pocketmine/scheduler/ServerScheduler.php @@ -28,7 +28,7 @@ use pocketmine\plugin\Plugin; use pocketmine\utils\ReversePriorityQueue; class ServerScheduler{ - + protected static $WORKERS = 3; /** * @var ReversePriorityQueue */ @@ -39,6 +39,11 @@ class ServerScheduler{ */ protected $tasks = array(); + /** @var \Pool */ + protected $asyncPool; + + protected $asyncTasks = 0; + /** @var int */ private $ids = 1; @@ -47,6 +52,7 @@ class ServerScheduler{ public function __construct(){ $this->queue = new ReversePriorityQueue(); + $this->asyncPool = new \Pool(self::$WORKERS, "Worker"); } /** @@ -58,6 +64,19 @@ class ServerScheduler{ return $this->addTask($task, -1, -1); } + /** + * Submits a asynchronous task to the Pool + * If the AsyncTask sets a result, you have to get it so it can be deleted + * + * @param AsyncTask $task + * + * @return void + */ + public function scheduleAsyncTask(AsyncTask $task){ + $this->asyncPool->submit($task); + ++$this->asyncTasks; + } + /** * @param Task $task * @param int $delay @@ -116,6 +135,8 @@ class ServerScheduler{ $task->cancel(); } $this->tasks = array(); + $this->asyncPool->shutdown(); + $this->asyncTasks = 0; } /** @@ -182,6 +203,16 @@ class ServerScheduler{ unset($this->tasks[$task->getTaskId()]); } } + + if($this->asyncTasks > 0){ //Garbage collector + $this->asyncPool->collect(function(AsyncTask $task){ + if($task->isCompleted() or ($task->isFinished() and !$task->hasResult())){ + --$this->asyncTasks; + return true; + } + return false; + }); + } } private function isReady($currentTicks){