From 18cd71818a33d629728867d3988a34e64c750afc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Schro=CC=88der?= Date: Tue, 20 May 2014 15:20:16 +0200 Subject: [PATCH] dedicated-server-api update --- .../DedicatedServer/Connection.php | 104 ++++++++++++------ .../Structures/ServerOptions.php | 31 ++++++ .../DedicatedServer/Structures/Status.php | 1 + .../DedicatedServer/Structures/Vote.php | 1 + .../DedicatedServer/Structures/VoteRatio.php | 2 + .../DedicatedServer/Xmlrpc/GbxRemote.php | 67 ++++++----- 6 files changed, 141 insertions(+), 65 deletions(-) mode change 100644 => 100755 application/core/Libs/Maniaplanet/DedicatedServer/Connection.php mode change 100644 => 100755 application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php b/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php old mode 100644 new mode 100755 index b21f7011..e3cd4a3f --- a/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Connection.php @@ -2081,31 +2081,23 @@ class Connection /** * Set new server options using the struct passed as parameters. * Mandatory fields: - * Name, Comment, Password, PasswordForSpectator, CallVoteRatio - * Optional fields: - * NextMaxPlayers, NextMaxSpectators, IsP2PUpload, IsP2PDownload, NextLadderMode, - * NextVehicleNetQuality, NextCallVoteTimeOut, AllowMapDownload, AutoSaveReplays, - * RefereePassword, RefereeMode, AutoSaveValidationReplays, HideServer, UseChangingValidationSeed, - * ClientInputsMaxLatency, DisableHorns, DisableServiceAnnounces, KeepPlayerSlots. + * Name, Comment, Password, PasswordForSpectator and CallVoteRatio. + * Ignored fields: + * LadderServerLimitMin, LadderServerLimitMax and those starting with Current. + * All other fields are optional and can be set to null to be ignored. * Only available to Admin. - * A change of NextMaxPlayers, NextMaxSpectators, NextLadderMode, NextVehicleNetQuality, - * NextCallVoteTimeOut or UseChangingValidationSeed requires a map restart to be taken into account. - * @param struct $options + * A change of any field starting with Next requires a map restart to be taken into account. + * @param Structures\ServerOptions $options * @param bool $multicall * @return bool * @throws InvalidArgumentException */ function setServerOptions($options, $multicall=false) { - if(!is_array($options) - || !(isset($options['Name']) && is_string($options['Name'])) - || !(isset($options['Comment']) && is_string($options['Comment'])) - || !(isset($options['Password']) && is_string($options['Password'])) - || !(isset($options['PasswordForSpectator']) && is_string($options['PasswordForSpectator'])) - || !(isset($options['CallVoteRatio']) && Structures\VoteRatio::isRatio($options['CallVoteRatio']))) + if(!($options instanceof Structures\ServerOptions && $options->isValid())) throw new InvalidArgumentException('options = '.print_r($options, true)); - return $this->execute(ucfirst(__FUNCTION__), array($options), $multicall); + return $this->execute(ucfirst(__FUNCTION__), array($options->toSetterArray()), $multicall); } /** @@ -2573,42 +2565,42 @@ class Connection /** * Get the script cloud variables of given object. * Only available to Admin. - * @param string $arg1 - * @param string $arg2 + * @param string $type + * @param string $id * @param bool $multicall * @return array * @throws InvalidArgumentException */ - function getScriptCloudVariables($arg1, $arg2, $multicall=false) + function getScriptCloudVariables($type, $id, $multicall=false) { - if(!is_string($arg1)) - throw new InvalidArgumentException('$arg1 = '.print_r($arg1, true)); - if(!is_string($arg2)) - throw new InvalidArgumentException('$arg2 = '.print_r($arg2, true)); + if(!is_string($type)) + throw new InvalidArgumentException('type = '.print_r($type, true)); + if(!is_string($id)) + throw new InvalidArgumentException('id = '.print_r($id, true)); - return $this->execute(ucfirst(__FUNCTION__), array($arg1, $arg2), $multicall); + return $this->execute(ucfirst(__FUNCTION__), array($type, $id), $multicall); } /** * Set the script cloud variables of given object. * Only available to Admin. - * @param string $arg1 - * @param string $arg2 - * @param struct $arg3 + * @param string $type + * @param string $id + * @param mixed[] $variables {mixed , ...} * @param bool $multicall * @return bool * @throws InvalidArgumentException */ - function setScriptCloudVariables($arg1, $arg2, $arg3, $multicall=false) + function setScriptCloudVariables($type, $id, $variables, $multicall=false) { - if(!is_string($arg1)) - throw new InvalidArgumentException('$arg1 = '.print_r($arg1, true)); - if(!is_string($arg2)) - throw new InvalidArgumentException('$arg2 = '.print_r($arg2, true)); - if(!is_struct($arg3)) - throw new InvalidArgumentException('$arg3 = '.print_r($arg3, true)); + if(!is_string($type)) + throw new InvalidArgumentException('type = '.print_r($type, true)); + if(!is_string($id)) + throw new InvalidArgumentException('id = '.print_r($id, true)); + if(!is_array($variables) || !$variables) + throw new InvalidArgumentException('variables = '.print_r($variables, true)); - return $this->execute(ucfirst(__FUNCTION__), array($arg1, $arg2, $arg3), $multicall); + return $this->execute(ucfirst(__FUNCTION__), array($type, $id, $variables), $multicall); } /** @@ -3548,7 +3540,7 @@ class Connection /** * Set as next maps the list of maps with the specified filenames, if they are present in the selection. * Only available to Admin. - * @param array $filenames Relative to the Maps path + * @param string[] $filenames Relative to the Maps path * @param bool $multicall * @return int Number of maps actually chosen * @throws InvalidArgumentException @@ -3942,6 +3934,46 @@ class Connection return $this->execute(ucfirst(__FUNCTION__), array(), $multicall); } + /** + * Join the server on lan. + * Only available on client. + * Only available to Admin. + * @param string $host IPv4 with optionally a port (eg. '192.168.1.42:2350') + * @param string $password + * @param bool $multicall + * @return bool + * @throws InvalidArgumentException + */ + function joinServerLan($host, $password='', $multicall=false) + { + if(!is_string($host)) + throw new InvalidArgumentException('host = '.print_r($host, true)); + if(!is_string($password)) + throw new InvalidArgumentException('password = '.print_r($password, true)); + + return $this->execute(ucfirst(__FUNCTION__), array(array('Server' => $host, 'ServerPassword' => $password)), $multicall); + } + + /** + * Join the server on internet. + * Only available on client. + * Only available to Admin. + * @param string $host Server login or IPv4 with optionally a port (eg. '192.168.1.42:2350') + * @param string $password + * @param bool $multicall + * @return bool + * @throws InvalidArgumentException + */ + function joinServerInternet($host, $password='', $multicall=false) + { + if(!is_string($host)) + throw new InvalidArgumentException('host = '.print_r($host, true)); + if(!is_string($password)) + throw new InvalidArgumentException('password = '.print_r($password, true)); + + return $this->execute(ucfirst(__FUNCTION__), array(array('Server' => $host, 'ServerPassword' => $password)), $multicall); + } + /** * Returns the login of the given player * @param mixed $player diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/ServerOptions.php b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/ServerOptions.php index 6b35c889..86ac17fb 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/ServerOptions.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/ServerOptions.php @@ -71,4 +71,35 @@ class ServerOptions extends AbstractStructure public $disableHorns; /** @var bool */ public $disableServiceAnnounces; + + /** + * @internal + * @return bool + */ + function isValid() + { + return is_string($this->name) + && is_string($this->comment) + && is_string($this->password) + && is_string($this->passwordForSpectator) + && VoteRatio::isRatio($this->callVoteRatio); + } + + /** + * @internal + * @return mixed[] + */ + function toSetterArray() + { + $out = array(); + foreach(get_object_vars($this) as $key => $value) + { + if(substr($key, 0, 7) == 'current' || $value === null) + continue; + if($key == 'nextUseChangingValidationSeed') + $key = 'useChangingValidationSeed'; + $out[ucfirst($key)] = $value; + } + return $out; + } } diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Status.php b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Status.php index 8f16cb95..652a7415 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Status.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Status.php @@ -15,6 +15,7 @@ class Status extends AbstractStructure const SYNCHRONIZATION = 3; const PLAY = 4; const EXITING = 6; + const LOCAL = 7; /** @var int */ public $code; diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Vote.php b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Vote.php index 9e63d5e8..c46e8d43 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Vote.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/Vote.php @@ -34,6 +34,7 @@ class Vote extends AbstractStructure } /** + * @internal * @return bool */ function isValid() diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/VoteRatio.php b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/VoteRatio.php index a90e4d21..878c0412 100644 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Structures/VoteRatio.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Structures/VoteRatio.php @@ -37,6 +37,7 @@ class VoteRatio extends AbstractStructure } /** + * @internal * @return bool */ function isValid() @@ -47,6 +48,7 @@ class VoteRatio extends AbstractStructure } /** + * @internal * @param float $ratio * @return bool */ diff --git a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php old mode 100644 new mode 100755 index 6b3d3c7a..bd407ae0 --- a/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php +++ b/application/core/Libs/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php @@ -16,10 +16,8 @@ class GbxRemote public static $sent; private $socket; - private $timeouts = array( - 'read' => 8000, - 'write' => 8000 - ); + private $readTimeout = array('sec' => 5, 'usec' => 0); + private $writeTimeout = array('sec' => 5, 'usec' => 0); private $requestHandle; private $callbacksBuffer = array(); private $multicallBuffer = array(); @@ -49,9 +47,15 @@ class GbxRemote function setTimeouts($read=0, $write=0) { if($read) - $this->timeouts['read'] = $read; + { + $this->readTimeout['sec'] = (int) ($read / 1000); + $this->readTimeout['usec'] = ($read % 1000) * 1000; + } if($write) - $this->timeouts['write'] = $write; + { + $this->writeTimeout['sec'] = (int) ($write / 1000); + $this->writeTimeout['usec'] = ($write % 1000) * 1000; + } } /** @@ -75,12 +79,13 @@ class GbxRemote if(!$this->socket) throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); + stream_set_read_buffer($this->socket, 0); stream_set_write_buffer($this->socket, 0); // handshake $header = $this->read(15); if($header === false) - throw new TransportException('Connection interrupted during handshake', TransportException::INTERRUPTED); + $this->onIoFailure('during handshake'); extract(unpack('Vsize/a*protocol', $header)); /** @var $size int */ @@ -185,10 +190,7 @@ class GbxRemote private function flush($waitResponse=false) { $r = array($this->socket); - $w = null; - $e = null; - $n = @stream_select($r, $w, $e, 0); - while($waitResponse || $n > 0) + while($waitResponse || @stream_select($r, $w, $e, 0) > 0) { list($handle, $xml) = $this->readMessage(); list($type, $value) = Request::decode($xml); @@ -203,10 +205,7 @@ class GbxRemote case 'call': $this->callbacksBuffer[] = $value; } - - if(!$waitResponse) - $n = @stream_select($r, $w, $e, 0); - }; + } } /** @@ -218,8 +217,7 @@ class GbxRemote { $header = $this->read(8); if($header === false) - throw new TransportException('Connection interrupted while reading header '.print_r(stream_get_meta_data($this->socket), true), TransportException::INTERRUPTED); - //throw new TransportException('Connection interrupted while reading header', TransportException::INTERRUPTED); + $this->onIoFailure('while reading header'); extract(unpack('Vsize/Vhandle', $header)); /** @var $size int */ @@ -232,8 +230,7 @@ class GbxRemote $data = $this->read($size); if($data === false) - //throw new TransportException('Connection interrupted while reading data', TransportException::INTERRUPTED); - throw new TransportException('Connection interrupted while reading data '.print_r(stream_get_meta_data($this->socket), true), TransportException::INTERRUPTED); + $this->onIoFailure('while reading data'); $this->lastNetworkActivity = time(); return array($handle, $data); @@ -245,10 +242,11 @@ class GbxRemote */ private function writeMessage($xml) { - $data = pack('V2a*', strlen($xml), ++$this->requestHandle, $xml); + if($this->requestHandle == (int) 0xffffffff) + $this->requestHandle = (int) 0x80000000; + $data = pack('V2', strlen($xml), ++$this->requestHandle).$xml; if(!$this->write($data)) - throw new TransportException('Connection interrupted while writing '.print_r(stream_get_meta_data($this->socket), true), TransportException::INTERRUPTED); - //throw new TransportException('Connection interrupted while writing', TransportException::INTERRUPTED); + $this->onIoFailure('while writing'); $this->lastNetworkActivity = time(); } @@ -258,7 +256,7 @@ class GbxRemote */ private function read($size) { - @stream_set_timeout($this->socket, 0, $this->timeouts['read'] * 1000); + @stream_set_timeout($this->socket, $this->readTimeout['sec'], $this->readTimeout['usec']); $data = ''; while(strlen($data) < $size) @@ -279,7 +277,8 @@ class GbxRemote */ private function write($data) { - @stream_set_timeout($this->socket, 0, $this->timeouts['write'] * 1000); + @stream_set_timeout($this->socket, $this->writeTimeout['sec'], $this->writeTimeout['usec']); + self::$sent += strlen($data); while(strlen($data) > 0) { @@ -287,22 +286,32 @@ class GbxRemote if($written === 0 || $written === false) return false; - fflush($this->socket); - $data = substr($data, $written); } - self::$sent += strlen($data); return true; } + + /** + * @param string $when + * @throws TransportException + */ + private function onIoFailure($when) + { + $meta = stream_get_meta_data($this->socket); + if($meta['timed_out']) + throw new TransportException('Connection timed out '.$when, TransportException::TIMED_OUT); + throw new TransportException('Connection interrupted '.$when, TransportException::INTERRUPTED); + } } class TransportException extends Exception { const NOT_INITIALIZED = 1; const INTERRUPTED = 2; - const WRONG_PROTOCOL = 3; - const PROTOCOL_ERROR = 4; + const TIMED_OUT = 3; + const WRONG_PROTOCOL = 4; + const PROTOCOL_ERROR = 5; } class MessageException extends Exception