From 85bc053fb3ff2f209f738093e3b824a515f5371e Mon Sep 17 00:00:00 2001 From: kremsy Date: Sat, 19 Apr 2014 00:32:34 +0200 Subject: [PATCH] switch to newboos dedicated api + idletime check --- .../DedicatedServer/Connection.php | 141 +++-- .../DedicatedServer/Xmlrpc/Base64.php | 15 +- .../DedicatedServer/Xmlrpc/Client.php | 481 ------------------ .../Xmlrpc/ClientMulticall.php | 47 -- .../DedicatedServer/Xmlrpc/Date.php | 68 --- .../DedicatedServer/Xmlrpc/Exception.php | 9 +- .../DedicatedServer/Xmlrpc/FatalException.php | 16 - .../DedicatedServer/Xmlrpc/GbxRemote.php | 321 ++++++++++++ .../DedicatedServer/Xmlrpc/Message.php | 189 ------- .../DedicatedServer/Xmlrpc/Request.php | 172 ++++++- .../DedicatedServer/Xmlrpc/Value.php | 144 ------ application/core/ManiaControl.php | 199 ++++---- .../core/ManiaExchange/ManiaExchangeList.php | 1 + 13 files changed, 645 insertions(+), 1158 deletions(-) delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Client.php delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/ClientMulticall.php delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Date.php delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/FatalException.php create mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Message.php delete mode 100644 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Value.php diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php b/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php index e55ff14c..f1f208d7 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php @@ -20,19 +20,19 @@ class Connection /** * XML-RPC client instance - * @var Xmlrpc\ClientMulticall + * @var Xmlrpc\GbxRemote */ protected $xmlrpcClient; /** * @param string $host * @param int $port - * @param int $timeout + * @param int $timeout (in ms) * @param string $user * @param string $password * @return Connection */ - static function factory($host = '127.0.0.1', $port = 5000, $timeout = 5, $user = 'SuperAdmin', $password = 'SuperAdmin') + static function factory($host = '127.0.0.1', $port = 5000, $timeout = 50, $user = 'SuperAdmin', $password = 'SuperAdmin') { $key = $host.':'.$port; if(!isset(self::$instances[$key])) @@ -56,6 +56,24 @@ class Connection } } + /** + * Change client timeouts + * @param int $read read timeout (in ms), null or 0 to leave unchanged + * @param int $write write timeout (in ms), null or 0 to leave unchanged + */ + function setTimeouts($read=null, $write=null) + { + $this->xmlrpcClient->setTimeouts($read, $write); + } + + /** + * @return int Network idle time in seconds + */ + function getIdleTime() + { + return $this->xmlrpcClient->getIdleTime(); + } + /** * @param string $host * @param int $port @@ -65,7 +83,7 @@ class Connection */ protected function __construct($host, $port, $timeout, $user, $password) { - $this->xmlrpcClient = new Xmlrpc\ClientMulticall($host, $port, $timeout); + $this->xmlrpcClient = new Xmlrpc\GbxRemote($host, $port, array('open' => $timeout)); $this->authenticate($user, $password); $this->setApiVersion('2013-04-16'); } @@ -86,9 +104,7 @@ class Connection */ function executeCallbacks() { - $this->xmlrpcClient->readCallbacks(); - $calls = $this->xmlrpcClient->getCallbackResponses(); - return $calls; + return $this->xmlrpcClient->getCallbacks(); } /** @@ -97,7 +113,7 @@ class Connection */ function executeMulticall() { - $this->xmlrpcClient->multiqueryIgnoreResult(); + $this->xmlrpcClient->multiquery(); } /** @@ -114,9 +130,7 @@ class Connection } else { - array_unshift($params, $methodName); - call_user_func_array(array($this->xmlrpcClient, 'query'), $params); - return $this->xmlrpcClient->getResponse(); + return $this->xmlrpcClient->query($methodName, $params); } } @@ -227,9 +241,9 @@ class Connection throw new InvalidArgumentException('vote->cmdParam = '.print_r($vote->cmdParam, true)); } - $tmpCmd = new Xmlrpc\Request($vote->cmdName, $vote->cmdParam); + $tmpCmd = Xmlrpc\Request::encode($vote->cmdName, $vote->cmdName); - return $this->execute(ucfirst(__FUNCTION__).'Ex', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); + return $this->execute(ucfirst(__FUNCTION__).'Ex', array($tmpCmd, $ratio, $timeout, $voters), $multicall); } /** @@ -264,9 +278,9 @@ class Connection throw new InvalidArgumentException('voters = '.print_r($voters, true)); } - $tmpCmd = new Xmlrpc\Request('Kick', array($login)); + $tmpCmd = Xmlrpc\Request::encode('Kick', array($login)); - return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); + return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); } /** @@ -301,9 +315,9 @@ class Connection throw new InvalidArgumentException('voters = '.print_r($voters, true)); } - $tmpCmd = new Xmlrpc\Request('Ban', array($login)); + $tmpCmd = Xmlrpc\Request::encode('Ban', array($login)); - return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); + return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); } /** @@ -333,9 +347,9 @@ class Connection throw new InvalidArgumentException('voters = '.print_r($voters, true)); } - $tmpCmd = new Xmlrpc\Request('RestartMap', array()); + $tmpCmd = Xmlrpc\Request::encode('RestartMap', array()); - return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); + return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); } /** @@ -365,9 +379,9 @@ class Connection throw new InvalidArgumentException('voters = '.print_r($voters, true)); } - $tmpCmd = new Xmlrpc\Request('NextMap', array()); + $tmpCmd = Xmlrpc\Request::encode('NextMap', array()); - return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); + return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); } /** @@ -832,11 +846,11 @@ class Connection { return $this->execute(ucfirst(__FUNCTION__)); } - + /** - * Opens a link in the client with the specified players. - * The parameters are the login of the client to whom the link to open is sent, the link url, and the 'LinkType' - * (0 in the external browser, 1 in the internal manialink browser). + * Opens a link in the client with the specified players. + * The parameters are the login of the client to whom the link to open is sent, the link url, and the 'LinkType' + * (0 in the external browser, 1 in the internal manialink browser). * Login can be a single login or a list of comma-separated logins. Only available to * @param Structures\Player|string|mixed[] $player * @param string $link @@ -859,7 +873,7 @@ class Connection { throw new InvalidArgumentException('linkType = '.print_r($linkType, true)); } - + return $this->execute('SendOpenLinkToLogin', array($login, $link, $linkType), $multicall); } @@ -1229,14 +1243,8 @@ class Connection } $inputData = file_get_contents($localFilename); - $data = new Xmlrpc\Base64($inputData); - if(strlen($data->getXml()) > 1024 * 1024 - 15) - { - throw new InvalidArgumentException('file is too big'); - } - return $this->execute(ucfirst(__FUNCTION__), array($filename, $data), $multicall); } @@ -1257,11 +1265,6 @@ class Connection $data = new Xmlrpc\Base64($data); - if(strlen($data->getXml()) > 1024 * 1024 - 15) - { - throw new InvalidArgumentException('data are too big'); - } - return $this->execute('WriteFile', array($filename, $data), $multicall); } @@ -1284,13 +1287,8 @@ class Connection { throw new InvalidArgumentException('filename = '.print_r($filename, true)); } - if(filesize($filename) > 4 * 1024) - { - throw new InvalidArgumentException('file is too big'); - } $inputData = file_get_contents($filename); - $data = new Xmlrpc\Base64($inputData); return $this->execute('TunnelSendDataToLogin', array($login, $data), $multicall); @@ -1541,9 +1539,9 @@ class Connection return $this->execute(ucfirst(__FUNCTION__), array($downloadRate, $uploadRate), $multicall); } - + /** - * Returns the list of tags and associated values set on this server. + * Returns the list of tags and associated values set on this server. * The list is an array of structures {string Name, string Value}. * Only available to Admin. * @return array @@ -1552,10 +1550,10 @@ class Connection { return $this->execute(ucfirst(__FUNCTION__)); } - + /** - * Set a tag and its value on the server. This method takes two parameters. - * The first parameter specifies the name of the tag, and the second one its value. + * Set a tag and its value on the server. This method takes two parameters. + * The first parameter specifies the name of the tag, and the second one its value. * Only available to Admin. * @param string $key * @param string $value @@ -1575,9 +1573,9 @@ class Connection } return $this->execute(ucfirst(__FUNCTION__), array($key, $value), $multicall); } - + /** - * Unset the tag with the specified name on the server. + * Unset the tag with the specified name on the server. * Only available to Admin. * @param string $key * @param bool $multicall @@ -1592,9 +1590,9 @@ class Connection } return $this->execute(ucfirst(__FUNCTION__), array($key), $multicall); } - + /** - * Reset all tags on the server. + * Reset all tags on the server. * Only available to Admin. * @param bool $multicall * @return bool @@ -1833,7 +1831,7 @@ class Connection } /** - * Get whether the server if a lobby, the number and maximum number of players currently managed by it. + * Get whether the server if a lobby, the number and maximum number of players currently managed by it. * The struct returned contains 4 fields IsLobby, LobbyPlayers, LobbyMaxPlayers, and LobbyPlayersLevel. * @return Structures\LobbyInfo */ @@ -1842,9 +1840,9 @@ class Connection $result = $this->execute(ucfirst(__FUNCTION__)); return Structures\LobbyInfo::fromArray($result); } - + /** - * Customize the clients 'leave server' dialog box. + * Customize the clients 'leave server' dialog box. * Parameters are: ManialinkPage, SendToServer url '#qjoin=login@title', * ProposeAddToFavorites and DelayQuitButton (in milliseconds). * Only available to Admin. @@ -2077,7 +2075,7 @@ class Connection * Returns a replay containing the data needed to validate the current best time of the player. * The parameter is the login of the player. * @param Structures\Player|string $player - * @return string base64 encoded + * @return string * @throws InvalidArgumentException */ function getValidationReplay($player) @@ -2086,7 +2084,7 @@ class Connection { throw new InvalidArgumentException('player must be set'); } - return $this->execute(ucfirst(__FUNCTION__), array($login)); + return $this->execute(ucfirst(__FUNCTION__), array($login))->scalar; } /** @@ -2639,7 +2637,7 @@ class Connection { return $this->execute(ucfirst(__FUNCTION__), array($rules), $multicall); } - + /** * Send commands to the mode script. * Only available to Admin. @@ -2651,9 +2649,9 @@ class Connection { return $this->execute(ucfirst(__FUNCTION__), array($commands), $multicall); } - + /** - * Change the settings and send commands to the mode script. + * Change the settings and send commands to the mode script. * Only available to Admin. * @param array $settings * @param array $commands @@ -3590,9 +3588,9 @@ class Connection } return $this->execute(ucfirst(__FUNCTION__), array($fakePlayerLogin), $multicall); } - + /** - * Returns the token infos for a player. + * Returns the token infos for a player. * The returned structure is { TokenCost, CanPayToken }. * @param Structures\Player|string $player * @return array @@ -4392,28 +4390,9 @@ class Connection } -/** - * Exception Dedicated to Query Error - */ -class QueryException extends \Exception -{ - -} - -/** - * Exception Dedicated to Connection Error - */ -class ConnectionException extends \Exception -{ - -} - /** * Exception Dedicated to Invalid Argument Error on Request Call */ -class InvalidArgumentException extends \Exception -{ - -} +class InvalidArgumentException extends \Exception {} ?> diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Base64.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Base64.php index 750ac1e7..ba5df913 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Base64.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Base64.php @@ -7,18 +7,17 @@ namespace Maniaplanet\DedicatedServer\Xmlrpc; -class Base64 +class Base64 { - public $data; + public $scalar; + public $xmlrpc_type = 'base64'; + /** + * @param string $data + */ function __construct($data) { - $this->data = $data; - } - - function getXml() - { - return ''.base64_encode($this->data).''; + $this->scalar = $data; } } diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Client.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Client.php deleted file mode 100644 index b32fab39..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Client.php +++ /dev/null @@ -1,481 +0,0 @@ - $f_v) - { - $repeater = intval(substr($f_v, 1)); - if ($repeater == 0) - { - $repeater = 1; - } - if ($f_v{1} == '*') - { - $repeater = count($ar) - $i; - } - if ($f_v{0} != 'd') - { - $i += $repeater; - continue; - } - $j = $i + $repeater; - for ($a = $i; $a < $j; ++$a) - { - $p = pack('d', $vals[$i]); - $p = strrev($p); - list($vals[$i]) = array_values(unpack('d1d', $p)); - ++$i; - } - } - $a = 0; - foreach ($ar as $ar_k => $ar_v) - { - $ar[$ar_k] = $vals[$a]; - ++$a; - } - return $ar; - } - } - } - } - - /** - * - * @param string $hostname - * @param int $port - * @param int $timeout In milliseconds - */ - function __construct($hostname, $port, $timeout = 50) - { - $this->socket = false; - $this->reqhandle = 0x80000000; - $this->timeout = $timeout; - $this->init($hostname, $port); - } - - function __destruct() - { - $this->terminate(); - } - - protected function init($hostname, $port) - { - - $this->bigEndianTest(); - - // open connection - $this->socket = @fsockopen($hostname, $port, $errno, $errstr, $this->timeout); - if (!$this->socket) - { - throw new FatalException("transport error - could not open socket (error: $errno, $errstr)", FatalException::NOT_INITIALIZED); - } - // handshake - $array_result = unpack('Vsize', fread($this->socket, 4)); - $size = $array_result['size']; - if ($size > 64) - { - throw new FatalException('transport error - wrong lowlevel protocol header', FatalException::OTHER); - } - $handshake = fread($this->socket, $size); - if ($handshake == 'GBXRemote 1') - { - $this->protocol = 1; - } - elseif ($handshake == 'GBXRemote 2') - { - $this->protocol = 2; - } - else - { - throw new FatalException('transport error - wrong lowlevel protocol version', FatalException::OTHER); - } - } - - function terminate() - { - if ($this->socket) - { - fclose($this->socket); - $this->socket = false; - } - } - - protected function sendRequest(Request $request) - { - $xml = $request->getXml(); - - @stream_set_timeout($this->socket, 0, $this->timeout * 1000 * 200); - // send request - $this->reqhandle++; - if ($this->protocol == 1) - { - $bytes = pack('Va*', strlen($xml), $xml); - } - else - { - $bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml); - } - - $bytes_to_write = strlen($bytes); - - // increase sent counter ... - self::$sent += $bytes_to_write; - - while ($bytes_to_write > 0) - { - $r = fwrite($this->socket, $bytes); - if ($r === false || $r == 0) - { - throw new FatalException('Connection interupted', FatalException::INTERRUPTED); - } - - $bytes_to_write -= $r; - if ($bytes_to_write == 0) - { - break; - } - - $bytes = substr($bytes, $r); - } - } - - protected function getResult() - { - $contents = ''; - $contents_length = 0; - do - { - $size = 0; - $recvhandle = 0; - @stream_set_timeout($this->socket, 0, $this->timeout * 1000 * 1000); - // Get result - if ($this->protocol == 1) - { - $contents = fread($this->socket, 4); - if (strlen($contents) == 0 || $contents === false) - { - throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); - } - $array_result = unpack('Vsize', $contents); - $size = $array_result['size']; - $recvhandle = $this->reqhandle; - } - else - { - $contents = fread($this->socket, 8); - if (strlen($contents) == 0 || $contents === false) - { - throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); - } - $array_result = unpack('Vsize/Vhandle', $contents); - $size = $array_result['size']; - $recvhandle = $array_result['handle']; - // -- amd64 support -- - $bits = sprintf('%b', $recvhandle); - if (strlen($bits) == 64) - { - $recvhandle = bindec(substr($bits, 32)); - } - } - - if ($recvhandle == 0 || $size == 0) - { - throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); - } - - if ($size > SIZE_MAX) - { - throw new Exception("transport error - answer too big ($size)", Exception::ANWSER_TOO_BIG); - } - - self::$received += $size; - - $contents = ''; - $contents_length = 0; - @stream_set_timeout($this->socket, 0, $this->timeout * 1000); - while ($contents_length < $size) - { - $contents .= fread($this->socket, $size-$contents_length); - $contents_length = strlen($contents); - } - - if (($recvhandle & 0x80000000) == 0) - { - // this is a callback, not our answer! handle= $recvhandle, xml-rpc= $contents - // just add it to the message list for the user to read - $new_cb_message = new Message($contents); - if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') - { - array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); - } - } - } - while ((int)$recvhandle != (int)$this->reqhandle); - - $this->message = new Message($contents); - if (!$this->message->parse()) - { - // XML error - throw new Exception('parse error. not well formed', Exception::OTHER); - } - // Is the message a fault? - if ($this->message->messageType == 'fault') - { - throw new Exception($this->message->faultString, $this->message->faultCode); - } - - return $this->message; - } - - - function query() - { - $args = func_get_args(); - $method = array_shift($args); - - if (!$this->socket || $this->protocol == 0) - { - throw new FatalException('deb4 transport error - Client not initialized', FatalException::NOT_INITIALIZED); - } - - $request = new Request($method, $args); - - // Check if request is larger than 1024 Kbytes - if ($request->getLength() > 1024*1024-8) - { - throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); - } - - $this->sendRequest($request); - return $this->getResult(); - } - - // Non-blocking query method: doesn't read the response - function queryIgnoreResult() - { - $args = func_get_args(); - $method = array_shift($args); - - if (!$this->socket || $this->protocol == 0) - { - throw new FatalException('deb 5transport error - Client not initialized', FatalException::NOT_INITIALIZED); - } - - $request = new Request($method, $args); - - // Check if the request is greater than 512 Kbytes to avoid errors - // If the method is system.multicall, make two calls (possibly recursively) - if ($request->getLength() > 1024*1024-8) - { - if ($method == 'system.multicall' && isset($args[0])) - { - $count = count($args[0]); - // If count is 1, query cannot be reduced - if ($count < 2) - { - throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); - } - $length = floor($count/2); - - $args1 = array_slice($args[0], 0, $length); - $args2 = array_slice($args[0], $length, ($count-$length)); - - $res1 = $this->queryIgnoreResult('system.multicall', $args1); - $res2 = $this->queryIgnoreResult('system.multicall', $args2); - return ($res1 && $res2); - } - // If the method is not a multicall, just stop - else - { - throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); - } - } - - $this->sendRequest($request); - } - - function getResponse() - { - // methodResponses can only have one param - return that - return $this->message->params[0]; - } - - function readCallbacks() - { - if (!$this->socket || $this->protocol == 0) - throw new FatalException('transport error - Client not initialized', FatalException::NOT_INITIALIZED); - if ($this->protocol == 1) - return false; - - @stream_set_timeout($this->socket, 0, $this->timeout * 20); // timeout 1 ms (to read available data) - // (assignment in arguments is forbidden since php 5.1.1) - $read = array($this->socket); - $write = NULL; - $except = NULL; - $nb = false; - - try - { - $nb = @stream_select($read, $write, $except, 0, $this->timeout * 20); - } - catch (\Exception $e) - { - if (strpos($e->getMessage(), 'Invalid CRT') !== false) - { - $nb = true; - } - elseif (strpos($e->getMessage(), 'Interrupted system call') !== false) - { - return; - } - else - { - throw $e; - } - } - - // workaround for stream_select bug with amd64 - if ($nb !== false) - { - $nb = count($read); - } - - while ($nb !== false && $nb > 0) - { - // Get result - $contents = ''; - while(strlen($contents) < 8){ - $contents .= fread($this->socket, 8 - strlen($contents)); - if (strlen($contents) == 0 || $contents === false) - { - var_dump("deb6 transport error"); - //throw new FatalException('deb6 transport error - connection interrupted!', FatalException::INTERRUPTED); - } - } - - $array_result = unpack('Vsize/Vhandle', $contents); - $size = $array_result['size']; - $recvhandle = $array_result['handle']; - - if ($recvhandle == 0 || $size == 0) - { - throw new FatalException('deb7 transport error - connection interrupted!', FatalException::INTERRUPTED); - } - if ($size > SIZE_MAX) - { - throw new Exception("transport error - answer too big ($size)", Exception::ANWSER_TOO_BIG); - } - - self::$received += $size; - - $contents = ''; - $contents_length = 0; - while ($contents_length < $size) - { - $contents .= fread($this->socket, $size-$contents_length); - $contents_length = strlen($contents); - } - - if (($recvhandle & 0x80000000) == 0) - { - // this is a callback. handle= $recvhandle, xml-rpc= $contents - //echo 'CALLBACK('.$contents_length.')[ '.$contents.' ]' . LF; - $new_cb_message = new Message($contents); - if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') - { - array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); - } - // flo: moved to end ... - // $something_received = true; - } - - // (assignment in arguments is forbidden since php 5.1.1) - $read = array($this->socket); - $write = NULL; - $except = NULL; - - try - { - $nb = @stream_select($read, $write, $except, 0, 0); // Notimeout, just flush the data - } - catch (\Exception $e) - { - if (strpos($e->getMessage(), 'Invalid CRT') !== false) - { - $nb = true; - } - else - { - throw $e; - } - } - - // workaround for stream_select bug with amd64 - if ($nb !== false) - { - $nb = count($read); - } - } - //stream_set_blocking($this->socket, true); - return !empty($this->cb_message); - } - - function getCallbackResponses() - { - // (look at the end of basic.php for an example) - $messages = $this->cb_message; - $this->cb_message = array(); - return $messages; - } -} - -?> diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/ClientMulticall.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/ClientMulticall.php deleted file mode 100644 index c18b1ccb..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/ClientMulticall.php +++ /dev/null @@ -1,47 +0,0 @@ - $methodName, 'params' => $args); - $this->calls[] = $struct; - - return (count($this->calls) - 1); - } - - function multiquery() - { - $result = array(); - if(count($this->calls)) - { - $result = parent::query('system.multicall', $this->calls); - $this->calls = array(); // reset for next calls - } - return $result; - } - - function multiqueryIgnoreResult() - { - if(count($this->calls)) - { - parent::queryIgnoreResult('system.multicall', $this->calls); - $this->calls = array(); // reset for next calls - } - } -} - -?> \ No newline at end of file diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Date.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Date.php deleted file mode 100644 index 46e1d7ea..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Date.php +++ /dev/null @@ -1,68 +0,0 @@ -parseTimestamp($time); - } - else - { - $this->parseIso($time); - } - } - - function parseTimestamp($timestamp) - { - $this->year = date('Y', $timestamp); - $this->month = date('m', $timestamp); - $this->day = date('d', $timestamp); - $this->hour = date('H', $timestamp); - $this->minute = date('i', $timestamp); - $this->second = date('s', $timestamp); - } - - function parseIso($iso) - { - $this->year = substr($iso, 0, 4); - $this->month = substr($iso, 4, 2); - $this->day = substr($iso, 6, 2); - $this->hour = substr($iso, 9, 2); - $this->minute = substr($iso, 12, 2); - $this->second = substr($iso, 15, 2); - } - - function getIso() - { - return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second; - } - - function getXml() - { - return ''.$this->getIso().''; - } - - function getTimestamp() - { - return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); - } -} - -?> \ No newline at end of file diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Exception.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Exception.php index 2e54e314..82c054e0 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Exception.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Exception.php @@ -7,11 +7,6 @@ namespace Maniaplanet\DedicatedServer\Xmlrpc; -class Exception extends \Exception -{ - const ANWSER_TOO_BIG = 1; - const REQUEST_TOO_BIG = 2; - const OTHER = 999; -} +class Exception extends \Exception {} -?> \ No newline at end of file +?> diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/FatalException.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/FatalException.php deleted file mode 100644 index 6a2590ff..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/FatalException.php +++ /dev/null @@ -1,16 +0,0 @@ - \ No newline at end of file diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php new file mode 100644 index 00000000..96618744 --- /dev/null +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php @@ -0,0 +1,321 @@ + 5, + 'read' => 1000, + 'write' => 1000 + ); + private $requestHandle; + private $callbacksBuffer = array(); + private $multicallBuffer = array(); + private $lastNetworkActivity = 0; + + /** + * @param string $host + * @param int $port + * @param int[string] $timeouts Override default timeouts for 'open' (in s), 'read' (in ms) and 'write' (in ms) socket operations + */ + function __construct($host, $port, $timeouts = array()) + { + $this->requestHandle = (int) 0x80000000; + $this->timeouts = array_merge($this->timeouts, $timeouts); + $this->connect($host, $port); + } + + function __destruct() + { + $this->terminate(); + } + + /** + * Change timeouts + * @param int $read read timeout (in ms), null or 0 to leave unchanged + * @param int $write write timeout (in ms), null or 0 to leave unchanged + */ + function setTimeouts($read=null, $write=null) + { + if($read) + $this->timeouts['read'] = $read; + if($write) + $this->timeouts['write'] = $write; + } + + /** + * @return int Network idle time in seconds + */ + function getIdleTime() + { + $this->assertConnected(); + return time() - $this->lastNetworkActivity; + } + + /** + * @param string $host + * @param int $port + * @throws TransportException + */ + private function connect($host, $port) + { + $this->socket = @fsockopen($host, $port, $errno, $errstr, $this->timeouts['open']); + stream_set_write_buffer($this->socket, 0); + if(!$this->socket) + throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); + + // handshake + $header = $this->read(15); + if($header === false) + throw new TransportException('Connection interrupted during handshake', TransportException::INTERRUPTED); + + extract(unpack('Vsize/a*protocol', $header)); + /** @var $size int */ + /** @var $protocol string */ + if($size != 11 || $protocol != 'GBXRemote 2') + throw new TransportException('Wrong protocol header', TransportException::WRONG_PROTOCOL); + $this->lastNetworkActivity = time(); + } + + function terminate() + { + if($this->socket) + { + fclose($this->socket); + $this->socket = null; + } + } + + /** + * @param string $method + * @param mixed[] $args + * @return mixed + * @throws MessageException + */ + function query($method, $args=array()) + { + $this->assertConnected(); + $xml = Request::encode($method, $args); + + if(strlen($xml) > self::MAX_REQUEST_SIZE-8) + { + if($method != 'system.multicall' || count($args) < 2) + throw new MessageException('Request too large', MessageException::REQUEST_TOO_LARGE); + + $mid = count($args) >> 1; + $this->query('system.multicall', array_slice($args, 0, $mid)); + $this->query('system.multicall', array_slice($args, $mid)); + } + + $this->writeMessage($xml); + return $this->flush(true); + } + + /** + * @param string $method + * @param mixed[] $args + */ + function addCall($method, $args) + { + $this->multicallBuffer[] = array( + 'methodName' => $method, + 'params' => $args + ); + } + + /** + * @return mixed + */ + function multiquery() + { + switch(count($this->multicallBuffer)) + { + case 0: + return; + case 1: + $call = array_shift($this->multicallBuffer); + return $this->query($call['methodName'], $call['params']); + default: + $result = $this->query('system.multicall', $this->multicallBuffer); + $this->multicallBuffer = array(); + return $result; + } + } + + /** + * @return mixed[] + */ + function getCallbacks() + { + $this->assertConnected(); + $this->flush(); + $cb = $this->callbacksBuffer; + $this->callbacksBuffer = array(); + return $cb; + } + + /** + * @throws TransportException + */ + private function assertConnected() + { + if(!$this->socket) + throw new TransportException('Connection not initialized', TransportException::NOT_INITIALIZED); + } + + /** + * @param bool $waitResponse + * @return mixed + * @throws FaultException + */ + private function flush($waitResponse=false) + { + $n = @stream_select($r=array($this->socket), $w=null, $e=null, 0); + while($waitResponse || $n > 0) + { + list($handle, $xml) = $this->readMessage(); + list($type, $value) = Request::decode($xml); + switch($type) + { + case 'fault': + throw FaultException::create($value['faultString'], $value['faultCode']); + case 'response': + if($handle == $this->requestHandle) + return $value; + break; + case 'call': + $this->callbacksBuffer[] = $value; + } + + if(!$waitResponse) + $n = @stream_select($r=array($this->socket), $w=null, $e=null, 0); + }; + } + + /** + * @return mixed[] + * @throws TransportException + * @throws MessageException + */ + private function readMessage() + { + $header = $this->read(8); + if($header === false) + throw new TransportException('Connection interrupted while reading header', TransportException::INTERRUPTED); + + extract(unpack('Vsize/Vhandle', $header)); + /** @var $size int */ + /** @var $handle int */ + if($size == 0 || $handle == 0) + throw new TransportException('Incorrect header', TransportException::PROTOCOL_ERROR); + + if($size > self::MAX_RESPONSE_SIZE) + throw new MessageException('Response too large', MessageException::RESPONSE_TOO_LARGE); + + $data = $this->read($size); + if($data === false) + throw new TransportException('Connection interrupted while reading data', TransportException::INTERRUPTED); + + $this->lastNetworkActivity = time(); + return array($handle, $data); + } + + /** + * @param string $xml + * @throws TransportException + */ + private function writeMessage($xml) + { + $data = pack('V2a*', strlen($xml), ++$this->requestHandle, $xml); + if(!$this->write($data)) + throw new TransportException('Connection interrupted while writing', TransportException::INTERRUPTED); + $this->lastNetworkActivity = time(); + } + + /** + * @param int $size + * @return boolean|string + */ + private function read($size) + { + @stream_set_timeout($this->socket, 0, $this->timeouts['read'] * 1000); + + $data = ''; + while(strlen($data) < $size) + { + $buf = fread($this->socket, $size - strlen($data)); + if($buf === '' || $buf === false) + return false; + $data .= $buf; + } + + self::$received += $size; + return $data; + } + + /** + * @param string $data + * @return boolean + */ + private function write($data) + { + @stream_set_timeout($this->socket, 0, $this->timeouts['write'] * 1000); + + while(strlen($data) > 0) + { + $written = fwrite($this->socket, $data); + if($written === 0 || $written === false) + return false; + + $data = substr($data, $written); + } + + self::$sent += strlen($data); + return true; + } +} + +class TransportException extends Exception +{ + const NOT_INITIALIZED = 1; + const INTERRUPTED = 2; + const WRONG_PROTOCOL = 3; + const PROTOCOL_ERROR = 4; +} + +class MessageException extends Exception +{ + const REQUEST_TOO_LARGE = 1; + const RESPONSE_TOO_LARGE = 2; +} + +class FaultException extends Exception +{ + static function create($faultString, $faultCode) + { + switch($faultString) + { + case 'Login unknown.': + return new LoginUnknownException($faultString, $faultCode); + } + + return new self($faultString, $faultCode); + } +} + +class LoginUnknownException extends FaultException {} + +?> diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Message.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Message.php deleted file mode 100644 index 22dedb22..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Message.php +++ /dev/null @@ -1,189 +0,0 @@ -message = $message; - } - - function parse() - { - - // first remove the XML declaration - $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message); - if (trim($this->message) == '') - { - return false; - } - $this->parser = xml_parser_create(); - // Set XML parser to take the case of tags into account - xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); - // Set XML parser callback functions - xml_set_object($this->parser, $this); - xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); - xml_set_character_data_handler($this->parser, 'cdata'); - if (!xml_parse($this->parser, $this->message)) - { - /* die(sprintf('GbxRemote XML error: %s at line %d', - xml_error_string(xml_get_error_code($this->_parser)), - xml_get_current_line_number($this->_parser))); */ - return false; - } - xml_parser_free($this->parser); - // Grab the error messages, if any - if ($this->messageType == 'fault') - { - $this->faultCode = $this->params[0]['faultCode']; - $this->faultString = $this->params[0]['faultString']; - } - return true; - } - - function tag_open($parser, $tag, $attr) - { - $this->currentTag = $tag; - switch ($tag) - { - case 'methodCall': - case 'methodResponse': - case 'fault': - $this->messageType = $tag; - break; - // Deal with stacks of arrays and structs - case 'data': // data is to all intents and purposes more interesting than array - $this->arrayStructsTypes[] = 'array'; - $this->arrayStructs[] = array(); - break; - case 'struct': - $this->arrayStructsTypes[] = 'struct'; - $this->arrayStructs[] = array(); - break; - } - } - - function cdata($parser, $cdata) - { - $this->currentTagContents .= $cdata; - } - - function tag_close($parser, $tag) - { - $valueFlag = false; - switch ($tag) - { - case 'int': - case 'i4': - $value = (int)trim($this->currentTagContents); - $this->currentTagContents = ''; - $valueFlag = true; - break; - case 'double': - $value = (double)trim($this->currentTagContents); - $this->currentTagContents = ''; - $valueFlag = true; - break; - case 'string': - $value = (string)trim($this->currentTagContents); - $this->currentTagContents = ''; - $valueFlag = true; - break; - case 'dateTime.iso8601': - $value = new Date(trim($this->currentTagContents)); - // $value = $iso->getTimestamp(); - $this->currentTagContents = ''; - $valueFlag = true; - break; - case 'value': - // If no type is indicated, the type is string - if (trim($this->currentTagContents) != '') { - $value = (string)$this->currentTagContents; - $this->currentTagContents = ''; - $valueFlag = true; - } - break; - case 'boolean': - $value = (boolean)trim($this->currentTagContents); - $this->currentTagContents = ''; - $valueFlag = true; - break; - case 'base64': - $value = base64_decode($this->currentTagContents); - $this->currentTagContents = ''; - $valueFlag = true; - break; - // Deal with stacks of arrays and structs - case 'data': - case 'struct': - $value = array_pop($this->arrayStructs); - array_pop($this->arrayStructsTypes); - $valueFlag = true; - break; - case 'member': - array_pop($this->currentStructName); - break; - case 'name': - $this->currentStructName[] = trim($this->currentTagContents); - $this->currentTagContents = ''; - break; - case 'methodName': - $this->methodName = trim($this->currentTagContents); - $this->currentTagContents = ''; - break; - } - - if ($valueFlag) - { - /* - if (!is_array($value) && !is_object($value)) { - $value = trim($value); - } - */ - if (count($this->arrayStructs) > 0) - { - // Add value to struct or array - if ($this->arrayStructsTypes[count($this->arrayStructsTypes)-1] == 'struct') - { - // Add to struct - $this->arrayStructs[count($this->arrayStructs)-1][$this->currentStructName[count($this->currentStructName)-1]] = $value; - } - else - { - // Add to array - $this->arrayStructs[count($this->arrayStructs)-1][] = $value; - } - } - else - { - // Just add as a paramater - $this->params[] = $value; - } - } - } -} - -?> \ No newline at end of file diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Request.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Request.php index eb9d179c..e9de8aa2 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Request.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Request.php @@ -7,36 +7,162 @@ namespace Maniaplanet\DedicatedServer\Xmlrpc; -class Request +if(extension_loaded('xmlrpc')) { - public $method; - public $args; - public $xml; - - function __construct($method, $args) + abstract class Request { - $this->method = $method; - $this->args = $args; - $this->xml = '' . $this->method . ''; - foreach ($this->args as $arg) + /** + * @param string $method + * @param mixed[] $args + * @return string + */ + static function encode($method, $args) { - $this->xml .= ''; - $v = new Value($arg); - $this->xml .= $v->getXml(); - $this->xml .= '' . LF; + return xmlrpc_encode_request($method, $args, array('encoding' => 'utf-8', 'verbosity' => 'no_white_space')); } - $this->xml .= ''; - } - function getLength() - { - return strlen($this->xml); - } + /** + * @param string $message + * @return mixed + * @throws ParseException + */ + static function decode($message) + { + $value = xmlrpc_decode_request($message, $method, 'utf-8'); + if($value === null) + throw new ParseException(); - function getXml() + if($method === null) + { + if(@xmlrpc_is_fault($value)) + return array('fault', $value); + return array('response', $value); + } + return array('call', array($method, $value)); + } + } +} +else +{ + abstract class Request { - return $this->xml; + const DATE_FORMAT = 'Ymd\TH:i:s'; + + /** + * @param string $method + * @param mixed[] $args + * @return string + */ + static function encode($method, $args) + { + $xml = ''.$method.''; + foreach($args as $arg) + $xml .= ''.self::encodeValue($arg).''; + $xml .= ''; + return $xml; + } + + /** + * @param mixed $v + * @return string + */ + private static function encodeValue($v) + { + switch(gettype($v)) + { + case 'boolean': + return ''.((int) $v).''; + case 'integer': + return ''.$v.''; + case 'double': + return ''.$v.''; + case 'string': + return ''.htmlspecialchars($v).''; + case 'object': + if($v instanceof Base64) + return ''.base64_encode($v->scalar).''; + if($v instanceof \DateTime) + return ''.$v->format(self::DATE_FORMAT).''; + $v = get_object_vars($v); + // fallthrough + case 'array': + $return = ''; + // pure array case + if(array_keys($v) == range(0, count($v) - 1)) + { + foreach($v as $item) + $return .= ''.self::encodeValue($item).''; + return ''.$return.''; + } + // else it's a struct + foreach($v as $name => $value) + $return .= ''.$name.''.self::encodeValue($value).''; + return ''.$return.''; + } + return ''; + } + + /** + * @param string $message + * @return mixed + * @throws ParseException + */ + static function decode($message) + { + $xml = @simplexml_load_string($message); + if(!$xml) + throw new ParseException(); + + if($xml->getName() == 'methodResponse') + { + if($xml->fault) + return array('fault', self::decodeValue($xml->fault->value)); + return array('response', self::decodeValue($xml->params->param->value)); + } + $params = array(); + foreach($xml->params->param as $param) + $params[] = self::decodeValue($param->value); + return array('call', array((string) $xml->methodName, $params)); + } + + /** + * @param \SimpleXMLElement $elt + * @return mixed + */ + private static function decodeValue($elt) + { + $elt = $elt->children(); + $elt = $elt[0]; + switch($elt->getName()) + { + case 'boolean': + return (bool) $elt; + case 'i4': + case 'int': + return (int) $elt; + case 'double': + return (double) $elt; + case 'string': + return (string) $elt; + case 'base64': + return new Base64(base64_decode($elt)); + case 'dateTime.iso8601': + return \DateTime::createFromFormat(self::DATE_FORMAT, (string) $elt); + case 'array': + $arr = array(); + foreach($elt->data->value as $v) + $arr[] = self::decodeValue($v); + return $arr; + case 'struct': + $struct = array(); + foreach($elt as $member) + $struct[(string) $member->name] = self::decodeValue($member->value); + return $struct; + } + } } } -?> \ No newline at end of file +class ParseException extends Exception {} + +?> diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Value.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Value.php deleted file mode 100644 index e9441f07..00000000 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/Value.php +++ /dev/null @@ -1,144 +0,0 @@ -data = $data; - if (!$type) - { - $type = $this->calculateType(); - } - $this->type = $type; - if ($type == 'struct') - { - // Turn all the values in the array into new Value objects - foreach ($this->data as $key => $value) - { - $this->data[$key] = new Value($value); - } - } - if ($type == 'array') - { - for ($i = 0, $j = count($this->data); $i < $j; $i++) - { - $this->data[$i] = new Value($this->data[$i]); - } - } - } - - function calculateType() - { - if ($this->data === true || $this->data === false) - { - return 'boolean'; - } - if (is_integer($this->data)) - { - return 'int'; - } - if (is_double($this->data)) - { - return 'double'; - } - // Deal with IXR object types base64 and date - if (is_object($this->data) && $this->data instanceof Date) - { - return 'date'; - } - if (is_object($this->data) && $this->data instanceof Base64) - { - return 'base64'; - } - // If it is a normal PHP object convert it into a struct - if (is_object($this->data)) - { - $this->data = get_object_vars($this->data); - return 'struct'; - } - if (!is_array($this->data)) - { - return 'string'; - } - // We have an array - is it an array or a struct? - if ($this->isStruct($this->data)) - { - return 'struct'; - } - else - { - return 'array'; - } - } - - function getXml() - { - // Return XML for this value - switch ($this->type) - { - case 'boolean': - return '' . ($this->data ? '1' : '0') . ''; - break; - case 'int': - return '' . $this->data . ''; - break; - case 'double': - return '' . $this->data . ''; - break; - case 'string': - return '' . htmlspecialchars($this->data) . ''; - break; - case 'array': - $return = '' . LF; - foreach ($this->data as $item) - { - $return .= ' ' . $item->getXml() . '' . LF; - } - $return .= ''; - return $return; - break; - case 'struct': - $return = '' . LF; - foreach ($this->data as $name => $value) - { - $return .= ' ' . $name . ''; - $return .= $value->getXml() . '' . LF; - } - $return .= ''; - return $return; - break; - case 'date': - case 'base64': - return $this->data->getXml(); - break; - } - return false; - } - - function isStruct($array) - { - // Nasty function to check if an array is a struct or not - $expected = 0; - foreach ($array as $key => $value) - { - if ((string)$key != (string)$expected) - { - return true; - } - $expected++; - } - return false; - } -} - -?> \ No newline at end of file diff --git a/application/core/ManiaControl.php b/application/core/ManiaControl.php index 1c4ac136..a097c415 100644 --- a/application/core/ManiaControl.php +++ b/application/core/ManiaControl.php @@ -23,8 +23,8 @@ use ManiaControl\Settings\SettingManager; use ManiaControl\Statistics\StatisticManager; use ManiaControl\Update\UpdateManager; use Maniaplanet\DedicatedServer\Connection; +use Maniaplanet\DedicatedServer\Transport\TransportException; use Maniaplanet\DedicatedServer\Xmlrpc\Exception; -use Maniaplanet\DedicatedServer\Xmlrpc\FatalException; require_once __DIR__ . '/Libs/Maniaplanet/DedicatedServer/Connection.php'; require_once __DIR__ . '/Libs/GbxDataFetcher/gbxdatafetcher.inc.php'; @@ -35,25 +35,25 @@ require_once __DIR__ . '/Libs/curl-easy/autoload.php'; /** * ManiaControl Server Controller for ManiaPlanet Server * - * @author steeffeen & kremsy + * @author steeffeen & kremsy * @copyright ManiaControl Copyright © 2014 ManiaControl Team - * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 + * @license http://www.gnu.org/licenses/ GNU General Public License, Version 3 */ class ManiaControl implements CommandListener, TimerListener { /* * Constants */ - const VERSION = '0.01'; - const API_VERSION = '2013-04-16'; - const MIN_DEDIVERSION = '2014-04-02_18_00'; - const OS_UNIX = 'Unix'; - const OS_WIN = 'Windows'; - const CONNECT_TIMEOUT = 50; - const SCRIPT_TIMEOUT = 20; - const URL_WEBSERVICE = 'http://ws.maniacontrol.com/'; + const VERSION = '0.01'; + const API_VERSION = '2013-04-16'; + const MIN_DEDIVERSION = '2014-04-02_18_00'; + const OS_UNIX = 'Unix'; + const OS_WIN = 'Windows'; + const CONNECT_TIMEOUT = 50; + const SCRIPT_TIMEOUT = 20; + const URL_WEBSERVICE = 'http://ws.maniacontrol.com/'; const SETTING_PERMISSION_SHUTDOWN = 'Shutdown ManiaControl'; - const SETTING_PERMISSION_RESTART = 'Restart ManiaControl'; - + const SETTING_PERMISSION_RESTART = 'Restart ManiaControl'; + /* * Public Properties */ @@ -79,7 +79,7 @@ class ManiaControl implements CommandListener, TimerListener { public $timerManager = null; public $fileReader = null; public $billManager = null; - + /* * Private Properties */ @@ -91,40 +91,53 @@ class ManiaControl implements CommandListener, TimerListener { public function __construct() { // Construct Error Handler $this->errorHandler = new ErrorHandler($this); - + $this->log('Loading ManiaControl v' . self::VERSION . '...'); - + // Load config $this->config = FileUtil::loadConfig('server.xml'); - + // Load ManiaControl Modules - $this->callbackManager = new CallbackManager($this); - $this->timerManager = new TimerManager($this); - $this->database = new Database($this); - $this->fileReader = new AsynchronousFileReader($this); - $this->billManager = new BillManager($this); - $this->settingManager = new SettingManager($this); - $this->statisticManager = new StatisticManager($this); - $this->manialinkManager = new ManialinkManager($this); - $this->actionsMenu = new ActionsMenu($this); - $this->chat = new Chat($this); - $this->commandManager = new CommandManager($this); - $this->server = new Server($this); + $this->callbackManager = new CallbackManager($this); + $this->timerManager = new TimerManager($this); + $this->database = new Database($this); + $this->fileReader = new AsynchronousFileReader($this); + $this->billManager = new BillManager($this); + $this->settingManager = new SettingManager($this); + $this->statisticManager = new StatisticManager($this); + $this->manialinkManager = new ManialinkManager($this); + $this->actionsMenu = new ActionsMenu($this); + $this->chat = new Chat($this); + $this->commandManager = new CommandManager($this); + $this->server = new Server($this); $this->authenticationManager = new AuthenticationManager($this); - $this->playerManager = new PlayerManager($this); - $this->mapManager = new MapManager($this); - $this->configurator = new Configurator($this); - $this->pluginManager = new PluginManager($this); - $this->updateManager = new UpdateManager($this); - + $this->playerManager = new PlayerManager($this); + $this->mapManager = new MapManager($this); + $this->configurator = new Configurator($this); + $this->pluginManager = new PluginManager($this); + $this->updateManager = new UpdateManager($this); + // Define Permission Levels $this->authenticationManager->definePermissionLevel(self::SETTING_PERMISSION_SHUTDOWN, AuthenticationManager::AUTH_LEVEL_SUPERADMIN); $this->authenticationManager->definePermissionLevel(self::SETTING_PERMISSION_RESTART, AuthenticationManager::AUTH_LEVEL_SUPERADMIN); - + // Register for commands $this->commandManager->registerCommandListener('version', $this, 'command_Version'); $this->commandManager->registerCommandListener('restart', $this, 'command_Restart', true); $this->commandManager->registerCommandListener('shutdown', $this, 'command_Shutdown', true); + + //Check connection every 2 minutes + $this->timerManager->registerTimerListening($this, 'checkConnection', 1000 * 180); + } + + /** + * Checks connection every xxx Minutes + * @param $time + */ + public function checkConnection($time){ + if($this->client->getIdleTime() > 300){ + $this->client->getServerName(); + } } /** @@ -165,7 +178,7 @@ class ManiaControl implements CommandListener, TimerListener { /** * Handle Version Command * - * @param array $chatCallback + * @param array $chatCallback * @param Player $player */ public function command_Version(array $chatCallback, Player $player) { @@ -176,7 +189,7 @@ class ManiaControl implements CommandListener, TimerListener { /** * Handle Restart AdminCommand * - * @param array $chatCallback + * @param array $chatCallback * @param Player $player */ public function command_Restart(array $chatCallback, Player $player) { @@ -190,7 +203,7 @@ class ManiaControl implements CommandListener, TimerListener { /** * Handle //shutdown command * - * @param array $chat + * @param array $chat * @param Player $player */ public function command_Shutdown(array $chat, Player $player) { @@ -210,7 +223,7 @@ class ManiaControl implements CommandListener, TimerListener { if ($message) { $this->log($message); } - + exit(); } @@ -220,32 +233,31 @@ class ManiaControl implements CommandListener, TimerListener { public function handleShutdown() { // OnShutdown callback $this->callbackManager->triggerCallback(CallbackManager::CB_ONSHUTDOWN); - + // Announce quit $this->chat->sendInformation('ManiaControl shutting down.'); - + if ($this->client) { try { // Hide manialinks $this->client->sendHideManialinkPage(); // Close the client connection $this->client->delete($this->server->ip, $this->server->port); - } - catch (FatalException $e) { + } catch(FatalException $e) { $this->errorHandler->triggerDebugNotice($e->getMessage() . " File: " . $e->getFile() . " Line: " . $e->getLine()); } } - + // Check and Trigger Fatal Errors $error = error_get_last(); if ($error && ($error['type'] & E_FATAL)) { $this->errorHandler->errorHandler($error['type'], $error['message'], $error['file'], $error['line']); } - + // Disable Garbage Collector $this->collectGarbage(); gc_disable(); - + $this->log('Quitting ManiaControl!'); exit(); } @@ -258,24 +270,23 @@ class ManiaControl implements CommandListener, TimerListener { public function restart($message = null) { // Shutdown callback $this->callbackManager->triggerCallback(CallbackManager::CB_ONSHUTDOWN); - + // Announce restart $this->chat->sendInformation('Restarting ManiaControl...'); if ($message) { $this->log($message); } - + // Hide widgets $this->client->sendHideManialinkPage(); - + $this->log('Restarting ManiaControl!'); - + // Execute start script in background if ($this->getOS(self::OS_UNIX)) { $command = 'sh ' . escapeshellarg(ManiaControlDir . '/ManiaControl.sh') . ' > /dev/null &'; exec($command); - } - else { + } else { $command = escapeshellarg(ManiaControlDir . "\ManiaControl.bat"); system($command); // TODO, windows stucks here as long controller is running } @@ -287,10 +298,10 @@ class ManiaControl implements CommandListener, TimerListener { */ public function run() { $this->log('Starting ManiaControl v' . self::VERSION . '!'); - + // Register shutdown handler register_shutdown_function(array($this, 'handleShutdown')); - + // Connect to server $this->connect(); @@ -300,59 +311,60 @@ class ManiaControl implements CommandListener, TimerListener { if($version->build < self::MIN_DEDIVERSION) { trigger_error("The server has version ".$version->build.", while at least ".self::MIN_DEDIVERSION." is required!", E_USER_ERROR); }*/ - + // OnInit callback $this->callbackManager->triggerCallback(CallbackManager::CB_ONINIT); - + // Load plugins $this->pluginManager->loadPlugins(); $this->updateManager->checkPluginsUpdate(); - + // AfterInit callback $this->callbackManager->triggerCallback(CallbackManager::CB_AFTERINIT); - + // Enable Garbage Collecting gc_enable(); $this->timerManager->registerTimerListening($this, 'collectGarbage', 1000 * 60); - + // Announce ManiaControl $this->chat->sendInformation('ManiaControl v' . self::VERSION . ' successfully started!'); - + // Loading finished $this->log('Loading completed!'); $this->log('Link: maniaplanet://#join=' . $this->server->login . '@' . $this->server->titleId); - + // Main loop - while (!$this->shutdownRequested) { + while(!$this->shutdownRequested) { $loopStart = microtime(true); - + // Disable script timeout set_time_limit(self::SCRIPT_TIMEOUT); - + try { // Manager callbacks $this->callbackManager->manageCallbacks(); - } - catch (FatalException $e) { + } catch(TransportException $e) { + $this->log("Connection interrupted!"); // TODO remove if ($this->errorHandler) { $this->errorHandler->triggerDebugNotice("Fatal Exception: " . $e->getMessage() . " Trace: " . $e->getTraceAsString()); } $this->quit($e->getMessage()); } - + + // Manage FileReader $this->fileReader->appendData(); - + // Yield for next tick $loopEnd = microtime(true); - - $sleepTime = (int) (2000 - ($loopEnd - $loopStart) * 1000000); + + $sleepTime = (int)(2000 - ($loopEnd - $loopStart) * 1000000); if ($sleepTime > 0) { usleep($sleepTime); } } - + // Shutdown $this->quit(); } @@ -370,60 +382,59 @@ class ManiaControl implements CommandListener, TimerListener { private function connect() { // Load remote client $success = $this->server->loadConfig(); - + $this->log("Connecting to server at {$this->server->config->host}:{$this->server->config->port}..."); - + try { - $this->client = Connection::factory($this->server->config->host, $this->server->config->port, self::CONNECT_TIMEOUT, - $this->server->config->login, $this->server->config->pass); - } - catch (Exception $e) { + $this->client = Connection::factory($this->server->config->host, $this->server->config->port, self::CONNECT_TIMEOUT, $this->server->config->login, $this->server->config->pass); + } catch(Exception $e) { trigger_error("Couldn't authenticate on server with user '{$this->server->config->login}'! " . $e->getMessage(), E_USER_ERROR); } - + // Enable callback system $this->client->enableCallbacks(true); - + // Wait for server to be ready try { if (!$this->server->waitForStatus(4)) { trigger_error("Server couldn't get ready!", E_USER_ERROR); } - } - catch (FatalException $e) { + } catch(FatalException $e) { // TODO remove if ($this->errorHandler) { $this->errorHandler->triggerDebugNotice("Fatal Exception: " . $e->getMessage() . " Trace: " . $e->getTraceAsString()); } $this->quit($e->getMessage()); } - + // Connect finished $this->log("Server Connection successfully established!"); - + // Hide old widgets $this->client->sendHideManialinkPage(); - + // Enable script callbacks if needed - if ($this->server->getGameMode() != 0) return; - + if ($this->server->getGameMode() != 0) { + return; + } + try { $scriptSettings = $this->client->getModeScriptSettings(); - } - catch (Exception $e) { + } catch(Exception $e) { if ($e->getMessage() == 'Not in script mode.') { return; } throw $e; } - - if (!array_key_exists('S_UseScriptCallbacks', $scriptSettings)) return; - + + if (!array_key_exists('S_UseScriptCallbacks', $scriptSettings)) { + return; + } + $scriptSettings['S_UseScriptCallbacks'] = true; try { $this->client->setModeScriptSettings($scriptSettings); - } - catch (Exception $e) { + } catch(Exception $e) { trigger_error("Couldn't set mode script settings to enable script callbacks. " . $e->getMessage()); return; } diff --git a/application/core/ManiaExchange/ManiaExchangeList.php b/application/core/ManiaExchange/ManiaExchangeList.php index 25bfad4c..417fc05c 100644 --- a/application/core/ManiaExchange/ManiaExchangeList.php +++ b/application/core/ManiaExchange/ManiaExchangeList.php @@ -111,6 +111,7 @@ class ManiaExchangeList implements CallbackListener, ManialinkPageAnswerListener $this->showManiaExchangeList($maps, $player); }; + // search for matching maps $this->maniaControl->mapManager->mxManager->getMapsAsync($function, $searchString, $author, $environment); }