diff --git a/src/PocketMine/CommandReader.php b/src/PocketMine/CommandReader.php index c51697bf8..605216d5d 100644 --- a/src/PocketMine/CommandReader.php +++ b/src/PocketMine/CommandReader.php @@ -77,7 +77,7 @@ class CommandReader extends \Thread{ }else{ $this->readline = false; $this->fp = fopen($this->stream, "r"); - stream_set_blocking($this->fp, 0); //Won't work on Windows :P + stream_set_blocking($this->fp, 1); //Non-blocking STDIN won't work on Windows } $lastLine = microtime(true); diff --git a/src/PocketMine/PocketMine.php b/src/PocketMine/PocketMine.php index 244f4403e..fbbb684c0 100644 --- a/src/PocketMine/PocketMine.php +++ b/src/PocketMine/PocketMine.php @@ -394,7 +394,7 @@ namespace PocketMine { $server = new Server(\PocketMine\PATH, \PocketMine\DATA, \PocketMine\PLUGIN_PATH); $server->start(); - kill(getmypid()); //Fix for ConsoleAPI being blocked + kill(getmypid()); exit(0); } diff --git a/src/PocketMine/Server.php b/src/PocketMine/Server.php index 32c71c013..09aee9b1e 100644 --- a/src/PocketMine/Server.php +++ b/src/PocketMine/Server.php @@ -61,6 +61,11 @@ class Server{ */ private static $instance = null; + /** + * @var bool + */ + private $isRunning = true; + /** * @var PluginManager */ @@ -132,6 +137,13 @@ class Server{ return "PocketMine-MP"; } + /** + * @return bool + */ + public function isRunning(){ + return $this->isRunning === true; + } + /** * @return string */ @@ -251,6 +263,41 @@ class Server{ return $this->getConfigBoolean("white-list", false); } + /** + * @return int + */ + public function getSpawnRadius(){ + return $this->getConfigInt("spawn-protection", 16); + } + + /** + * @return bool + */ + public function getAllowFlight(){ + return $this->getConfigBoolean("allow-flight", false); + } + + /** + * @return bool + */ + public function isHardcore(){ + return $this->getConfigBoolean("hardcore", false); + } + + /** + * @return int + */ + public function getDefaultGamemode(){ + return $this->getConfigInt("gamemode", 0) & 0b11; + } + + /** + * @return string + */ + public function getMotd(){ + return $this->getConfigString("motd", ""); + } + /** * @return PluginManager */ @@ -421,7 +468,6 @@ class Server{ "server-name" => "Minecraft: PE Server", "motd" => "Welcome @player to this server!", "server-port" => 19132, - "server-type" => "normal", "memory-limit" => "128M", "white-list" => false, "announce-player-achievements" => true, @@ -484,6 +530,7 @@ class Server{ $this->consoleSender = new ConsoleCommandSender(); $this->commandMap = new SimpleCommandMap($this); $this->pluginManager = new PluginManager($this, $this->commandMap); + $this->pluginManager->subscribeToPermission(Player::BROADCAST_CHANNEL_ADMINISTRATIVE, $this->consoleSender); $this->pluginManager->registerInterface("PocketMine\\Plugin\\FolderPluginLoader"); $this->pluginManager->loadPlugins($this->pluginPath); @@ -576,6 +623,10 @@ class Server{ return false; } + public function shutdown(){ + $this->isRunning = false; + } + /** * Starts the PocketMine-MP server and starts processing ticks and packets */ @@ -608,11 +659,24 @@ class Server{ }else{ $this->tickProcessor(); } + + foreach(Player::getAll() as $player){ + $player->kick("server stop"); + } + + foreach(Level::getAll() as $level){ + $level->unload(true); + } + + $this->pluginManager->disablePlugins(); + + $this->console->kill(); + //TODO: kill scheduler } private function tickProcessorWindows(){ $lastLoop = 0; - while(true){ + while($this->isRunning){ if(($packet = $this->interface->readPacket()) instanceof Packet){ if(EventHandler::callEvent(new PacketReceiveEvent($packet)) !== Event::DENY){ $this->handlePacket($packet); @@ -632,7 +696,7 @@ class Server{ private function tickProcessor(){ $lastLoop = 0; - while(true){ + while($this->isRunning){ if(($packet = $this->interface->readPacket()) instanceof Packet){ if(EventHandler::callEvent(new PacketReceiveEvent($packet)) !== Event::DENY){ $this->handlePacket($packet); diff --git a/src/PocketMine/command/Command.php b/src/PocketMine/command/Command.php index 82215aab9..e930dcc90 100644 --- a/src/PocketMine/command/Command.php +++ b/src/PocketMine/command/Command.php @@ -24,6 +24,8 @@ */ namespace PocketMine\Command; +use PocketMine\Player; +use PocketMine\Server; use PocketMine\Utils\TextFormat; abstract class Command{ @@ -290,6 +292,24 @@ abstract class Command{ * @param bool $sendToSource */ public static function broadcastCommandMessage(CommandSender $source, $message, $sendToSource = true){ + $result = $source->getName() .": " . $message; + //Command minecarts or command blocks are not implemented + + $users = Server::getInstance()->getPluginManager()->getPermissionSubscriptions(Player::BROADCAST_CHANNEL_ADMINISTRATIVE); + $colored = TextFormat::GRAY . TextFormat::ITALIC . "[$result".TextFormat::GRAY . TextFormat::ITALIC."]"; + if($sendToSource === true and !($source instanceof ConsoleCommandSender)){ + $source->sendMessage($message); + } + + foreach($users as $user){ + if($user instanceof CommandSender){ + if($user instanceof ConsoleCommandSender){ + $user->sendMessage($result); + }elseif($user !== $source){ + $user->sendMessage($colored); + } + } + } } } \ No newline at end of file diff --git a/src/PocketMine/command/ConsoleCommandSender.php b/src/PocketMine/command/ConsoleCommandSender.php index d23e5a864..abba252a0 100644 --- a/src/PocketMine/command/ConsoleCommandSender.php +++ b/src/PocketMine/command/ConsoleCommandSender.php @@ -23,6 +23,7 @@ namespace PocketMine\Command; use PocketMine\Permission\PermissibleBase; use PocketMine\Permission\PermissionAttachment; +use PocketMine\Player; use PocketMine\Plugin\Plugin; use PocketMine\Server; diff --git a/src/PocketMine/command/SimpleCommandMap.php b/src/PocketMine/command/SimpleCommandMap.php index bfdf32bab..3a64a72f4 100644 --- a/src/PocketMine/command/SimpleCommandMap.php +++ b/src/PocketMine/command/SimpleCommandMap.php @@ -22,6 +22,7 @@ namespace PocketMine\Command; use PocketMine\Command\Defaults\PluginsCommand; +use PocketMine\Command\Defaults\StopCommand; use PocketMine\Command\Defaults\VanillaCommand; use PocketMine\Command\Defaults\VersionCommand; use PocketMine\Command\Defaults\SeedCommand; @@ -51,6 +52,7 @@ class SimpleCommandMap implements CommandMap{ $this->register("pocketmine", new PluginsCommand("plugins")); $this->register("pocketmine", new SeedCommand("seed")); $this->register("pocketmine", new HelpCommand("help")); + $this->register("pocketmine", new StopCommand("stop")); } diff --git a/src/PocketMine/command/defaults/StopCommand.php b/src/PocketMine/command/defaults/StopCommand.php new file mode 100644 index 000000000..ffeaff9b9 --- /dev/null +++ b/src/PocketMine/command/defaults/StopCommand.php @@ -0,0 +1,58 @@ +setPermission("pocketmine.command.stop"); + } + + public function execute(CommandSender $sender, $currentAlias, array $args){ + if(!$this->testPermission($sender)){ + return true; + } + + Command::broadcastCommandMessage($sender, "Stopping the server..."); + + $reason = implode(" ", $args); + if($reason !== ""){ + foreach(Player::getAll() as $player){ + $player->kick($reason); + } + } + + Server::getInstance()->shutdown(); + + return true; + } +} \ No newline at end of file diff --git a/src/PocketMine/network/ThreadedHandler.php b/src/PocketMine/network/ThreadedHandler.php index fb22e1c78..f8cffa68f 100644 --- a/src/PocketMine/network/ThreadedHandler.php +++ b/src/PocketMine/network/ThreadedHandler.php @@ -117,12 +117,13 @@ class ThreadedHandler extends \Thread{ public function run(){ $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); socket_set_option($this->socket, SOL_SOCKET, SO_BROADCAST, 1); //Allow sending broadcast messages - if(socket_bind($this->socket, $this->serverip, $this->port) === true){ + if(@socket_bind($this->socket, $this->serverip, $this->port) === true){ socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 0); @socket_set_option($this->socket, SOL_SOCKET, SO_SNDBUF, 1024 * 1024 * 2); //2MB @socket_set_option($this->socket, SOL_SOCKET, SO_RCVBUF, 1024 * 1024); //1MB }else{ - console("[SEVERE] Couldn't bind to $this->serverip:" . $this->port, true, true, 0); + console("[SEVERE] **** FAILED TO BIND TO ".$this->serverip.":" . $this->port."!", true, true, 0); + console("[SEVERE] Perhaps a server is already running on that port?", true, true, 0); exit(1); } socket_set_nonblock($this->socket); diff --git a/src/PocketMine/permission/PermissibleBase.php b/src/PocketMine/permission/PermissibleBase.php index 5a9e8986e..96e05203f 100644 --- a/src/PocketMine/permission/PermissibleBase.php +++ b/src/PocketMine/permission/PermissibleBase.php @@ -197,7 +197,7 @@ class PermissibleBase implements Permissible{ * @param bool $invert * @param PermissionAttachment $attachment */ - public function calculateChildPermissions(array $children, $invert, PermissionAttachment $attachment){ + public function calculateChildPermissions(array $children, $invert, $attachment){ foreach(array_keys($children) as $name){ $perm = Server::getInstance()->getPluginManager()->getPermission($name); $value = $invert === true ? !$children[$name] : $children[$name]; diff --git a/src/PocketMine/permission/PermissionAttachment.php b/src/PocketMine/permission/PermissionAttachment.php index 7f084fef9..6d8b3e77e 100644 --- a/src/PocketMine/permission/PermissionAttachment.php +++ b/src/PocketMine/permission/PermissionAttachment.php @@ -49,10 +49,7 @@ class PermissionAttachment{ * @param Permissible $permissible */ public function __construct(Plugin $plugin, Permissible $permissible){ - if($plugin === null){ - trigger_error("Plugin cannot be null", E_USER_WARNING); - return; - }elseif(!$plugin->isEnabled()){ + if(!$plugin->isEnabled()){ trigger_error("Plugin ".$plugin->getDescription()->getName()." is disabled", E_USER_WARNING); return; } diff --git a/src/PocketMine/permission/PermissionAttachmentInfo.php b/src/PocketMine/permission/PermissionAttachmentInfo.php index af70a9211..2a2b3a6f1 100644 --- a/src/PocketMine/permission/PermissionAttachmentInfo.php +++ b/src/PocketMine/permission/PermissionAttachmentInfo.php @@ -49,11 +49,8 @@ class PermissionAttachmentInfo{ * @param PermissionAttachment $attachment * @param bool $value */ - public function __construct(Permissible $permissible, $permission, PermissionAttachment $attachment, $value){ - if($permissible === null){ - trigger_error("Permissible may not be null", E_USER_WARNING); - return; - }elseif($permission === null){ + public function __construct(Permissible $permissible, $permission, $attachment, $value){ + if($permission === null){ trigger_error("Permission may not be null", E_USER_WARNING); return; } diff --git a/src/PocketMine/scheduler/ServerAllTaskCanceller.php b/src/PocketMine/scheduler/ServerAllTaskCanceller.php new file mode 100644 index 000000000..31e7ce396 --- /dev/null +++ b/src/PocketMine/scheduler/ServerAllTaskCanceller.php @@ -0,0 +1,81 @@ +temp = $temp; + $this->pending = $pending; + $this->runners = $runners; + } + + public function run(){ + foreach($this->runners as $index => $task){ + $task->cancel0(); + if($task->isSync()){ + unset($this->runners[$index]); + } + } + while($this->temp->count() > 0){ + $this->temp->pop(); + } + while($this->pending->count() > 0){ + $this->pending->pop(); + } + } + + /** + * @param ServerTask[] $collection + * + * @return bool + */ + private function check($collection){ + foreach($collection as $index => $task){ + if($task->getTaskId() === $this->taskId){ + $task->cancel0(); + unset($collection[$index]); + if($task->isSync()){ + unset($this->runners[$this->taskId]); + } + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/PocketMine/scheduler/ServerAsyncTask.php b/src/PocketMine/scheduler/ServerAsyncTask.php index f68ff335e..39d88e45a 100644 --- a/src/PocketMine/scheduler/ServerAsyncTask.php +++ b/src/PocketMine/scheduler/ServerAsyncTask.php @@ -53,7 +53,7 @@ class ServerAsyncTask extends ServerTask{ return; } $workers[] = new ServerWorker($asyncTask, $thread); - }, $this->workers, $thread, $this); + }, $thread, $this); parent::run(); diff --git a/src/PocketMine/scheduler/ServerPluginTaskCanceller.php b/src/PocketMine/scheduler/ServerPluginTaskCanceller.php new file mode 100644 index 000000000..355a2bedd --- /dev/null +++ b/src/PocketMine/scheduler/ServerPluginTaskCanceller.php @@ -0,0 +1,79 @@ +plugin = $plugin; + $this->temp = $temp; + $this->pending = $pending; + $this->runners = $runners; + } + + public function run(){ + $this->check($this->temp); + $this->check($this->pending); + } + + /** + * @param ServerTask[] $collection + * + * @return bool + */ + private function check($collection){ + foreach($collection as $index => $task){ + if($task->getOwner() === $this->plugin){ + $task->cancel0(); + unset($collection[$index]); + if($task->isSync()){ + unset($this->runners[$task->getTaskId()]); + } + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/PocketMine/scheduler/ServerScheduler.php b/src/PocketMine/scheduler/ServerScheduler.php index 2e09fec86..60c79e09d 100644 --- a/src/PocketMine/scheduler/ServerScheduler.php +++ b/src/PocketMine/scheduler/ServerScheduler.php @@ -53,7 +53,7 @@ class ServerScheduler{ private $temp = array(); /** - * @var \Threaded + * @var ServerTask[] */ private $runners; @@ -77,14 +77,14 @@ class ServerScheduler{ /** * @param int $workers */ - public function __construct($workers = 2){ + public function __construct($workers = 8){ self::$instance = $this; $this->head = new ServerTask(); $this->tail = new ServerTask(); $this->pending = new \SplPriorityQueue(); $this->temp = array(); $this->runners = new \Threaded(); - //TODO: $this->executor = new \Pool($workers); + $this->executor = new TaskPool($workers); } @@ -256,7 +256,7 @@ class ServerScheduler{ } //TODO - $task = new ServerTask(null, new ServerTaskCanceller($taskId)); + $task = new ServerTask(null, new ServerTaskCanceller($taskId, $this->temp, $this->pending, $this->runners)); $this->handle($task, 0); for($taskPending = $this->head->getNext(); $taskPending !== null; $taskPending = $taskPending->getNext()){ if($taskPending === $task){ @@ -277,7 +277,7 @@ class ServerScheduler{ } //TODO - $task = new ServerTask(null, new ServerPluginTaskCanceller($plugin)); + $task = new ServerTask(null, new ServerPluginTaskCanceller($plugin, $this->temp, $this->pending, $this->runners)); $this->handle($task, 0); for($taskPending = $this->head->getNext(); $taskPending !== null; $taskPending = $taskPending->getNext()){ if($taskPending === $task){ @@ -300,7 +300,7 @@ class ServerScheduler{ */ public function cancelAllTasks(){ //TODO - $task = new ServerTask(null, new ServerAllTaskCanceller()); + $task = new ServerTask(null, new ServerAllTaskCanceller($this->temp, $this->pending, $this->runners)); $this->handle($task, 0); for($taskPending = $this->head->getNext(); $taskPending !== null; $taskPending = $taskPending->getNext()){ if($taskPending === $task){ diff --git a/src/PocketMine/scheduler/ServerTaskCanceller.php b/src/PocketMine/scheduler/ServerTaskCanceller.php new file mode 100644 index 000000000..9b1e415bf --- /dev/null +++ b/src/PocketMine/scheduler/ServerTaskCanceller.php @@ -0,0 +1,73 @@ +taskId = $taskId; + $this->temp = $temp; + $this->pending = $pending; + $this->runners = $runners; + } + + public function run(){ + if(!$this->check($this->temp)){ + $this->check($this->pending); + } + } + + /** + * @param ServerTask[] $collection + * + * @return bool + */ + private function check($collection){ + foreach($collection as $index => $task){ + if($task->getTaskId() === $this->taskId){ + $task->cancel0(); + unset($collection[$index]); + if($task->isSync()){ + unset($this->runners[$this->taskId]); + } + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/PocketMine/scheduler/TaskPool.php b/src/PocketMine/scheduler/TaskPool.php new file mode 100644 index 000000000..66efc2120 --- /dev/null +++ b/src/PocketMine/scheduler/TaskPool.php @@ -0,0 +1,34 @@ +workers as $worker){ + $worker->shutdown(); + } + } +} \ No newline at end of file diff --git a/src/PocketMine/scheduler/TaskWorker.php b/src/PocketMine/scheduler/TaskWorker.php new file mode 100644 index 000000000..ec4ef9105 --- /dev/null +++ b/src/PocketMine/scheduler/TaskWorker.php @@ -0,0 +1,31 @@ +