From fa899e87a86806d16dc295e3fe340388d0d9d6cd Mon Sep 17 00:00:00 2001 From: kremsy Date: Sun, 21 Jun 2015 20:45:48 +0200 Subject: [PATCH] added new socketmanager, testing and exception ahndling not done yet --- ManiaControl.php | 2 +- core/Callbacks/CallbackManager.php | 5 +- core/Callbacks/Listening.php | 3 +- core/ManiaControl.php | 14 ++- core/Players/PlayerManager.php | 2 + core/Sockets/SocketManager.php | 190 ++++++++++++++++++++++++----- 6 files changed, 179 insertions(+), 37 deletions(-) diff --git a/ManiaControl.php b/ManiaControl.php index 80e8d3a9..e5e7b6f1 100644 --- a/ManiaControl.php +++ b/ManiaControl.php @@ -21,7 +21,7 @@ define('MANIACONTROL_PATH', __DIR__ . DIRECTORY_SEPARATOR); define('ManiaControlDir', MANIACONTROL_PATH); // Set process settings -ini_set('memory_limit', '64M'); +ini_set('memory_limit', '512M'); if (!ini_get('date.timezone') && function_exists('date_default_timezone_set')) { date_default_timezone_set('UTC'); } diff --git a/core/Callbacks/CallbackManager.php b/core/Callbacks/CallbackManager.php index cd0c6f63..828e27d5 100644 --- a/core/Callbacks/CallbackManager.php +++ b/core/Callbacks/CallbackManager.php @@ -153,8 +153,6 @@ class CallbackManager { return $this->removeCallbackListener($this->callbackListenings, $listener); } - //TODO better name (used only in customvotesPlugin) - /** * Remove the Callback Listener from the given Listeners Array * @@ -212,6 +210,9 @@ class CallbackManager { // Manage Timings $this->maniaControl->getTimerManager()->manageTimings(); + // Manage Socket Tickets + $this->maniaControl->getSocketManager()->tick(); + // Server Callbacks if (!$this->maniaControl->getClient()) { return; diff --git a/core/Callbacks/Listening.php b/core/Callbacks/Listening.php index 26722661..310981ec 100644 --- a/core/Callbacks/Listening.php +++ b/core/Callbacks/Listening.php @@ -57,9 +57,10 @@ class Listening { * Trigger the Listener's Method with the given Array of Params * * @param array $params + * @return mixed */ public function triggerCallbackWithParams(array $params) { - call_user_func_array($this->getUserFunction(), $params); + return call_user_func_array($this->getUserFunction(), $params); } /** diff --git a/core/ManiaControl.php b/core/ManiaControl.php index bf523dc4..39c3b4cb 100644 --- a/core/ManiaControl.php +++ b/core/ManiaControl.php @@ -24,6 +24,7 @@ use ManiaControl\Players\PlayerManager; use ManiaControl\Plugins\PluginManager; use ManiaControl\Server\Server; use ManiaControl\Settings\SettingManager; +use ManiaControl\Sockets\SocketManager; use ManiaControl\Statistics\StatisticManager; use ManiaControl\Update\UpdateManager; use ManiaControl\Utils\CommandLineHelper; @@ -166,7 +167,8 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener { private $requestQuitMessage = null; /** @var EchoManager $echoManager */ - private $echoManager = null; + private $echoManager = null; + private $socketManager = null; /** * Construct a new ManiaControl instance @@ -186,6 +188,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener { $this->fileReader = new AsynchronousFileReader($this); $this->billManager = new BillManager($this); $this->settingManager = new SettingManager($this); + $this->socketManager = new SocketManager($this); $this->statisticManager = new StatisticManager($this); $this->manialinkManager = new ManialinkManager($this); $this->actionsMenu = new ActionsMenu($this); @@ -293,6 +296,15 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener { return $this->echoManager; } + /** + * Return the socket manager + * + * @return SocketManager + */ + public function getSocketManager() { + return $this->socketManager; + } + /** * Return the chat * diff --git a/core/Players/PlayerManager.php b/core/Players/PlayerManager.php index 1752a3c2..81e7c97b 100644 --- a/core/Players/PlayerManager.php +++ b/core/Players/PlayerManager.php @@ -112,6 +112,8 @@ class PlayerManager implements CallbackListener, TimerListener, EchoListener { // Echo Warn Command (Usage: sendEcho json_encode("player" => "loginName") $this->maniaControl->getEchoManager()->registerEchoListener(self::ECHO_WARN_PLAYER, $this, function ($params) { $this->playerActions->warnPlayer(null, $params->player, false); + + return "abcdef"; }); } diff --git a/core/Sockets/SocketManager.php b/core/Sockets/SocketManager.php index ca4ed2fc..f96f71c7 100644 --- a/core/Sockets/SocketManager.php +++ b/core/Sockets/SocketManager.php @@ -1,25 +1,36 @@ + * @copyright 2014-2015 ManiaControl Team + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + */ +class SocketManager { /** @var ManiaControl $maniaControl */ private $maniaControl = null; + + /** @var LoopInterface $loop */ + private $loop = null; + /** @var Listening[] $socketListenings */ private $socketListenings = array(); + /** @var Server $socket */ + private $socket = null; + + /** * Create a new Socket Handler Instance * @@ -28,37 +39,152 @@ class SocketHandler { public function __construct(ManiaControl $maniaControl) { $this->maniaControl = $maniaControl; + $this->createSocket(); } - public function createSocket() { - $loop = Factory::create(); - $server = stream_socket_server('tcp://127.0.0.1:19999'); - stream_set_blocking($server, 0); + /** + * Register a new Socket Listener + * + * @param string $callbackName + * @param SocketListener $listener + * @param string $method + * @return bool + */ + public function registerSocketListener($echoName, SocketListener $listener, $method) { + if (!Listening::checkValidCallback($listener, $method)) { + $listenerClass = get_class($listener); + trigger_error("Given Listener '{$listenerClass}' can't handle Callback '{$echoName}': No callable Method '{$method}'!"); + return false; + } - $loop->addReadStream($server, function ($server) use ($loop) { - $conn = stream_socket_accept($server); - $data = "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nHi\n"; - $loop->addWriteStream($conn, function ($conn) use (&$data, $loop) { - $written = fwrite($conn, $data); - if ($written === strlen($data)) { - fclose($conn); - $loop->removeStream($conn); - } else { - $data = substr($data, 0, $written); - } + if (!array_key_exists($echoName, $this->socketListenings)) { + $this->socketListenings[$echoName] = new Listening($listener, $method); + } else { + //TODO say which is already listening and other stuff + trigger_error("Only one Listener can listen on a specific Socket Message"); + } + + return true; + } + + /** + * Trigger a specific Callback + * + * @param mixed $callback + */ + public function triggerSocketCallback($callbackName) { + if (!array_key_exists($callbackName, $this->socketListenings)) { + return null; + } + + $params = func_get_args(); + $params = array_slice($params, 1, null, true); + + $listening = $this->socketListenings[$callbackName]; + /** @var Listening $listening */ + return $listening->triggerCallbackWithParams($params); + } + + + /** + * Unregister a Socket Listener + * + * @param SocketListener $listener + * @return bool + */ + public function unregisterEchoListener(SocketListener $listener) { + return $this->removeSocketListener($this->socketListenings, $listener); + } + + + /** + * Remove the Socket Listener from the given Listeners Array + * + * @param Listening[] $listeningsArray + * @param SocketListener $listener + * @return bool + */ + private function removeSocketListener(array &$listeningsArray, SocketListener $listener) { + $removed = false; + foreach ($listeningsArray as &$listening) { + if ($listening->listener === $listener) { + unset($listening); + $removed = true; + } + } + return $removed; + } + + + /** + * Creates The Socket + */ + private function createSocket() { + try { + $this->loop = Factory::create(); + $this->socket = new Server($this->loop); + + $this->socket->on('error', function ($e) { + //TODO error handling + var_dump($e); }); - }); - $loop->addPeriodicTimer(5, function () { - $memory = memory_get_usage() / 1024; - $formatted = number_format($memory, 3) . 'K'; - echo "Current memory usage: {$formatted}\n"; - }); + $this->socket->on('connection', function (Connection $connection) { + $buffer = ''; + $connection->on('data', function ($data) use (&$buffer, &$connection) { + $buffer .= $data; + $arr = explode("\n", $buffer, 2); // much haxy. + while (count($arr) == 2 && strlen($arr[1]) >= (int) $arr[0]) { + // received full message + $len = (int) $arr[0]; + $msg = substr($arr[1], 0, $len); // clip msg + $buffer = substr($buffer, strlen((string) $len) + 1 /* newline */ + $len); // clip buffer - $loop->tick(); + //TODO pass and port management + // Decode Message + $data = openssl_decrypt($msg, 'aes-192-cbc', 'testpass123', OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX'); + $data = json_decode($data); + + if ($data == null) { + $data = array("error" => true, "data" => "Data is not provided as an valid AES-196-encrypted encrypted JSON"); + } else if (!property_exists($data, "method") || !property_exists($data, "data")) { + $data = array("error" => true, "data" => "Invalid Message"); + } else { + $answer = $this->triggerSocketCallback($data->method, $data); + //Prepare Response + if (!$answer) { + $data = array("error" => true, "data" => "No listener or response on the given Message"); + } else { + $data = array("error" => false, "data" => $answer); + } + } + + //Encode, Encrypt and Send Response + $data = json_encode($data); + $data = openssl_encrypt($data, 'aes-192-cbc', 'testpass123', OPENSSL_RAW_DATA, 'kZ2Kt0CzKUjN2MJX'); + $connection->write(strlen($data) . "\n" . $data); + + // next msg + $arr = explode("\n", $buffer, 2); + } + }); + }); + //TODO port + $this->socket->listen(19999, getHostByName(getHostName())); // exceptions are just thrown right? why does it not work with local ip? because you bind to your loopback adapter k + + // so that aint it.. xD^^ maybe because it is not in an apache environemnt or smth + // this lib should never run in such an env but the periodictimer works? :O thats actually cool xD ye xD + } catch (ConnectionException $e) { + //TODO proper handling + var_dump($e); + } } - public function tick() { + /** + * Processes Data on every ManiaControl Tick, don't call this Method + */ + public function tick() { + $this->loop->tick(); } } \ No newline at end of file