├── .gitignore ├── src └── Maniaplanet │ └── DedicatedServer │ ├── Xmlrpc │ ├── Exception.php │ ├── Base64.php │ ├── FaultException.php │ ├── Request.php │ └── GbxRemote.php │ ├── Structures │ ├── Player.php │ ├── Mod.php │ ├── Tag.php │ ├── PlayerAnswer.php │ ├── PlayerBan.php │ ├── TokenInfos.php │ ├── LadderLimits.php │ ├── Command.php │ ├── ForcedSkin.php │ ├── ScriptSettings.php │ ├── LobbyInfo.php │ ├── Version.php │ ├── Status.php │ ├── PlayerNetInfo.php │ ├── ZoneRanking.php │ ├── Bill.php │ ├── Skin.php │ ├── FileDesc.php │ ├── Team.php │ ├── Music.php │ ├── PlayerRanking.php │ ├── SystemInfos.php │ ├── LadderStats.php │ ├── ScriptInfo.php │ ├── NetworkStats.php │ ├── Vote.php │ ├── Map.php │ ├── VoteRatio.php │ ├── AbstractStructure.php │ ├── GameInfos.php │ ├── PlayerDetailedInfo.php │ ├── PlayerInfo.php │ └── ServerOptions.php │ └── Connection.php ├── README.md ├── composer.json └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | /vendor 3 | /composer.lock 4 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Xmlrpc/Exception.php: -------------------------------------------------------------------------------- 1 | scalar = $data; 21 | } 22 | 23 | /** 24 | * @return string 25 | */ 26 | function __toString() 27 | { 28 | return $this->scalar; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/ZoneRanking.php: -------------------------------------------------------------------------------- 1 | path); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/Bill.php: -------------------------------------------------------------------------------- 1 | packDesc = FileDesc::fromArray($object->packDesc); 24 | return $object; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/FileDesc.php: -------------------------------------------------------------------------------- 1 | fileName = str_replace("\xEF\xBB\xBF", '', $object->fileName); 24 | return $object; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ManiaPlanet dedicated server SDK 2 | ================================ 3 | 4 | [![Latest Stable Version](https://poser.pugx.org/maniaplanet/dedicated-server-api/v/stable.png)](https://packagist.org/packages/maniaplanet/dedicated-server-api) 5 | [![Total Downloads](https://poser.pugx.org/maniaplanet/dedicated-server-api/downloads.png)](https://packagist.org/packages/maniaplanet/dedicated-server-api) 6 | 7 | This library allows you to connect to Maniaplanet dedicated server trough the XML-RPC interface. 8 | 9 | Installation 10 | ------------ 11 | 12 | We use [Composer](https://getcomposer.org/): 13 | 14 | ``` 15 | { 16 | "require": { 17 | "maniaplanet/dedicated-server-api": "^6.0" 18 | } 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/Team.php: -------------------------------------------------------------------------------- 1 | file = str_replace("\xEF\xBB\xBF", '', $object->file); 26 | return $object; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/PlayerRanking.php: -------------------------------------------------------------------------------- 1 | playerRankings = ZoneRanking::fromArrayOfArray($object->playerRankings); 34 | return $object; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/ScriptInfo.php: -------------------------------------------------------------------------------- 1 | paramDescs = ScriptSettings::fromArrayOfArray($object->paramDescs); 32 | $object->commandDescs = Command::fromArrayOfArray($object->commandDescs); 33 | return $object; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/NetworkStats.php: -------------------------------------------------------------------------------- 1 | playerNetInfos = PlayerNetInfo::fromArrayOfArray($object->playerNetInfos); 35 | return $object; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/Vote.php: -------------------------------------------------------------------------------- 1 | cmdName = $cmdName; 33 | $this->cmdParam = $cmdParam; 34 | } 35 | 36 | /** 37 | * @internal 38 | * @return bool 39 | */ 40 | function isValid() 41 | { 42 | return is_string($this->cmdName) 43 | && is_array($this->cmdParam); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/Map.php: -------------------------------------------------------------------------------- 1 | fileName = str_replace("\xEF\xBB\xBF", '', $object->fileName); 52 | return $object; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/VoteRatio.php: -------------------------------------------------------------------------------- 1 | command = $command; 36 | $this->ratio = $ratio; 37 | $this->param = ''; 38 | } 39 | 40 | /** 41 | * @internal 42 | * @return bool 43 | */ 44 | function isValid() 45 | { 46 | return is_string($this->command) 47 | && is_string($this->param) 48 | && self::isRatio($this->ratio); 49 | } 50 | 51 | /** 52 | * @internal 53 | * @param float $ratio 54 | * @return bool 55 | */ 56 | static function isRatio($ratio) 57 | { 58 | return is_float($ratio) && ($ratio === -1. || ($ratio >= 0. && $ratio <= 1.)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/AbstractStructure.php: -------------------------------------------------------------------------------- 1 | $value) { 20 | $result[$key] = static::fromArray($value); 21 | } 22 | return $result; 23 | } 24 | 25 | public static function fromArray($array) 26 | { 27 | if (!is_array($array)) { 28 | return $array; 29 | } 30 | 31 | $object = new static; 32 | foreach ($array as $key => $value) { 33 | $object->{lcfirst($key)} = $value; 34 | } 35 | return $object; 36 | } 37 | 38 | public static function getPropertyFromArray($array, $property) 39 | { 40 | return array_map(get_called_class() . '::extractProperty', $array, array_fill(0, count($array), $property)); 41 | } 42 | 43 | protected static function extractProperty($element, $property) 44 | { 45 | if (!is_a($element, get_called_class()) || !property_exists($element, $property)) { 46 | throw new \InvalidArgumentException('property ' . $property . ' does not exists in class: ' . get_called_class()); 47 | } 48 | 49 | return $element->$property; 50 | } 51 | 52 | function toArray() 53 | { 54 | $out = []; 55 | foreach (get_object_vars($this) as $key => $value) { 56 | $out[ucfirst($key)] = $value; 57 | } 58 | return $out; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/GameInfos.php: -------------------------------------------------------------------------------- 1 | avatar = FileDesc::fromArray($object->avatar); 60 | $object->skins = Skin::fromArrayOfArray($object->skins); 61 | $object->ladderStats = LadderStats::fromArray($object->ladderStats); 62 | return $object; 63 | } 64 | 65 | /** 66 | * @return string[] 67 | */ 68 | function getArrayFromPath() 69 | { 70 | return explode('|', $this->path); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/PlayerInfo.php: -------------------------------------------------------------------------------- 1 | forceSpectator = $object->flags % 10; // 0, 1 or 2 70 | $object->isReferee = (bool)(intval($object->flags / 10) % 10); 71 | $object->isPodiumReady = (bool)(intval($object->flags / 100) % 10); 72 | $object->isUsingStereoscopy = (bool)(intval($object->flags / 1000) % 10); 73 | $object->isManagedByAnOtherServer = (bool)(intval($object->flags / 10000) % 10); 74 | $object->isServer = (bool)(intval($object->flags / 100000) % 10); 75 | $object->hasPlayerSlot = (bool)(intval($object->flags / 1000000) % 10); 76 | $object->isBroadcasting = (bool)(intval($object->flags / 10000000) % 10); 77 | $object->hasJoinedGame = (bool)(intval($object->flags / 100000000) % 10); 78 | //Details spectatorStatus 79 | $object->spectator = (bool)($object->spectatorStatus % 10); 80 | $object->temporarySpectator = (bool)(intval($object->spectatorStatus / 10) % 10); 81 | $object->pureSpectator = (bool)(intval($object->spectatorStatus / 100) % 10); 82 | $object->autoTarget = (bool)(intval($object->spectatorStatus / 1000) % 10); 83 | $object->currentTargetId = intval($object->spectatorStatus / 10000); 84 | 85 | return $object; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Structures/ServerOptions.php: -------------------------------------------------------------------------------- 1 | name) 82 | && is_string($this->comment) 83 | && is_string($this->password) 84 | && is_string($this->passwordForSpectator) 85 | && is_int($this->nextCallVoteTimeOut) 86 | && VoteRatio::isRatio($this->callVoteRatio); 87 | } 88 | 89 | /** 90 | * @internal 91 | * @return mixed[] 92 | */ 93 | public function toSetterArray() 94 | { 95 | $out = []; 96 | foreach (get_object_vars($this) as $key => $value) { 97 | if (str_starts_with($key, 'current') || $value === null) { 98 | continue; 99 | } 100 | if ($key === 'nextUseChangingValidationSeed') { 101 | $key = 'useChangingValidationSeed'; 102 | } 103 | $out[ucfirst($key)] = $value; 104 | } 105 | return $out; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Xmlrpc/FaultException.php: -------------------------------------------------------------------------------- 1 | 'utf-8', 15 | 'escaping' => 'markup', 16 | 'verbosity' => 'no_white_space' 17 | ]; 18 | 19 | /** 20 | * @param string $method 21 | * @param mixed[] $args 22 | * @return string 23 | */ 24 | static function encode($method, $args, $escape = true) 25 | { 26 | $opts = self::$options; 27 | if (!$escape) { 28 | $opts['escaping'] = []; 29 | } 30 | return xmlrpc_encode_request($method, $args, $opts); 31 | } 32 | 33 | /** 34 | * @param string $message 35 | * @return mixed 36 | * @throws ParseException 37 | */ 38 | static function decode($message) 39 | { 40 | $value = xmlrpc_decode_request($message, $method, 'utf-8'); 41 | if ($value === null) { 42 | throw new ParseException(); 43 | } 44 | 45 | if ($method === null) { 46 | if (is_array($value) && xmlrpc_is_fault($value)) { 47 | return ['fault', $value]; 48 | } 49 | return ['response', $value]; 50 | } 51 | return ['call', [$method, $value]]; 52 | } 53 | } 54 | } else { 55 | abstract class Request 56 | { 57 | const DATE_FORMAT = 'Ymd\TH:i:s'; 58 | 59 | /** 60 | * @param string $method 61 | * @param mixed[] $args 62 | * @return string 63 | */ 64 | static function encode($method, $args, $escape = true) 65 | { 66 | $xml = '' . self::escape($method, $escape) . ''; 67 | if (!$args) { 68 | return $xml . ''; 69 | } 70 | 71 | $xml .= ''; 72 | foreach ($args as $arg) { 73 | $xml .= '' . self::encodeValue($arg, $escape) . ''; 74 | } 75 | return $xml . ''; 76 | } 77 | 78 | /** 79 | * @param mixed $v 80 | * @return string 81 | */ 82 | private static function encodeValue($v, $escape = true) 83 | { 84 | switch (gettype($v)) { 85 | case 'boolean': 86 | return '' . ((int)$v) . ''; 87 | case 'integer': 88 | return '' . $v . ''; 89 | case 'double': 90 | return '' . $v . ''; 91 | case 'string': 92 | if (strlen($v) === 0) { 93 | return ''; 94 | } 95 | return '' . self::escape($v, $escape) . ''; 96 | case 'NULL': 97 | return ''; 98 | case 'object': 99 | if ($v instanceof Base64) { 100 | if (!$v->scalar) { 101 | return ''; 102 | } 103 | return '' . base64_encode($v->scalar) . ''; 104 | } 105 | if ($v instanceof \DateTime) { 106 | return '' . $v->format(self::DATE_FORMAT) . ''; 107 | } 108 | $v = get_object_vars($v); 109 | // fallthrough 110 | case 'array': 111 | // empty array case 112 | if (!$v) { 113 | return ''; 114 | } 115 | $return = ''; 116 | // pure array case 117 | if (array_keys($v) === range(0, count($v) - 1)) { 118 | foreach ($v as $item) { 119 | $return .= '' . self::encodeValue($item, $escape) . ''; 120 | } 121 | return '' . $return . ''; 122 | } 123 | // else it's a struct 124 | foreach ($v as $name => $value) { 125 | $return .= '' . self::escape($name, $escape) . '' . self::encodeValue($value, $escape) . ''; 126 | } 127 | return '' . $return . ''; 128 | } 129 | return ''; 130 | } 131 | 132 | /** 133 | * @param string $str 134 | * @param bool $escape 135 | * @return string 136 | */ 137 | private static function escape($str, $escape = true) 138 | { 139 | if ($escape) { 140 | return '', ']]]]>', $str) . ']]>'; 141 | } 142 | return $str; 143 | } 144 | 145 | /** 146 | * @param string $message 147 | * @return mixed 148 | * @throws ParseException 149 | */ 150 | static function decode($message) 151 | { 152 | $xml = @simplexml_load_string($message); 153 | if (!$xml) { 154 | throw new ParseException(); 155 | } 156 | 157 | if ($xml->getName() == 'methodResponse') { 158 | if ($xml->fault) { 159 | return ['fault', self::decodeValue($xml->fault->value)]; 160 | } 161 | return ['response', self::decodeValue($xml->params->param->value)]; 162 | } 163 | $params = []; 164 | foreach ($xml->params->param as $param) { 165 | $params[] = self::decodeValue($param->value); 166 | } 167 | return ['call', [(string)$xml->methodName, $params]]; 168 | } 169 | 170 | /** 171 | * @param \SimpleXMLElement $elt 172 | * @return mixed 173 | */ 174 | private static function decodeValue($elt) 175 | { 176 | $elt = $elt->children(); 177 | $elt = $elt[0]; 178 | switch ($elt->getName()) { 179 | case 'boolean': 180 | return (bool)(int)$elt; 181 | case 'i4': 182 | case 'int': 183 | return (int)$elt; 184 | case 'double': 185 | return (double)$elt; 186 | case 'string': 187 | return (string)$elt; 188 | case 'base64': 189 | return new Base64(base64_decode($elt)); 190 | case 'dateTime.iso8601': 191 | return \DateTime::createFromFormat(self::DATE_FORMAT, (string)$elt); 192 | case 'array': 193 | $arr = []; 194 | foreach ($elt->data->value as $v) { 195 | $arr[] = self::decodeValue($v); 196 | } 197 | return $arr; 198 | case 'struct': 199 | $struct = []; 200 | foreach ($elt as $member) { 201 | $struct[(string)$member->name] = self::decodeValue($member->value); 202 | } 203 | return $struct; 204 | } 205 | } 206 | } 207 | } 208 | 209 | class ParseException extends Exception 210 | { 211 | } 212 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Xmlrpc/GbxRemote.php: -------------------------------------------------------------------------------- 1 | 5, 'usec' => 0]; 20 | private $writeTimeout = ['sec' => 5, 'usec' => 0]; 21 | private $requestHandle; 22 | private $callbacksBuffer = []; 23 | private $multicallBuffer = []; 24 | private $lastNetworkActivity = 0; 25 | 26 | /** 27 | * @param string $host 28 | * @param int $port 29 | * @param int $timeout Timeout when opening connection 30 | */ 31 | function __construct($host, $port, $timeout = 5) 32 | { 33 | $this->requestHandle = (int)0x80000000; 34 | $this->connect($host, $port, $timeout); 35 | } 36 | 37 | /** 38 | * @param string $host 39 | * @param int $port 40 | * @param int $timeout 41 | * @throws TransportException 42 | */ 43 | private function connect($host, $port, $timeout) 44 | { 45 | $this->socket = @fsockopen($host, $port, $errno, $errstr, $timeout); 46 | if (!$this->socket) { 47 | throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); 48 | } 49 | 50 | stream_set_read_buffer($this->socket, 0); 51 | stream_set_write_buffer($this->socket, 0); 52 | 53 | // handshake 54 | $header = $this->read(15); 55 | if ($header === false) { 56 | if (!is_resource($this->socket)) { 57 | $this->onIoFailure('socket closed during handshake'); 58 | } 59 | $this->onIoFailure(sprintf('during handshake (%s)', socket_strerror(socket_last_error()))); 60 | } 61 | 62 | extract(unpack('Vsize/a*protocol', $header)); 63 | /** @var $size int */ 64 | /** @var $protocol string */ 65 | if ($size != 11 || $protocol != 'GBXRemote 2') { 66 | throw new TransportException('Wrong protocol header', TransportException::WRONG_PROTOCOL); 67 | } 68 | $this->lastNetworkActivity = time(); 69 | } 70 | 71 | /** 72 | * @param int $size 73 | * @return boolean|string 74 | */ 75 | private function read($size) 76 | { 77 | @stream_set_timeout($this->socket, $this->readTimeout['sec'], $this->readTimeout['usec']); 78 | 79 | $data = ''; 80 | while (strlen($data) < $size) { 81 | $buf = @fread($this->socket, $size - strlen($data)); 82 | if ($buf === '' || $buf === false) { 83 | return false; 84 | } 85 | $data .= $buf; 86 | } 87 | 88 | self::$received += $size; 89 | return $data; 90 | } 91 | 92 | /** 93 | * @param string $when 94 | * @throws TransportException 95 | */ 96 | private function onIoFailure($when) 97 | { 98 | $meta = stream_get_meta_data($this->socket); 99 | if ($meta['timed_out']) { 100 | throw new TransportException('Connection timed out ' . $when, TransportException::TIMED_OUT); 101 | } 102 | throw new TransportException('Connection interrupted ' . $when, TransportException::INTERRUPTED); 103 | } 104 | 105 | function __destruct() 106 | { 107 | $this->terminate(); 108 | } 109 | 110 | public function terminate() 111 | { 112 | if ($this->socket) { 113 | fclose($this->socket); 114 | $this->socket = null; 115 | } 116 | } 117 | 118 | /** 119 | * Change timeouts 120 | * @param int $read read timeout (in ms), 0 to leave unchanged 121 | * @param int $write write timeout (in ms), 0 to leave unchanged 122 | */ 123 | public function setTimeouts($read = 0, $write = 0) 124 | { 125 | if ($read) { 126 | $this->readTimeout['sec'] = (int)($read / 1000); 127 | $this->readTimeout['usec'] = ($read % 1000) * 1000; 128 | } 129 | if ($write) { 130 | $this->writeTimeout['sec'] = (int)($write / 1000); 131 | $this->writeTimeout['usec'] = ($write % 1000) * 1000; 132 | } 133 | } 134 | 135 | /** 136 | * @return int Network idle time in seconds 137 | */ 138 | function getIdleTime() 139 | { 140 | $this->assertConnected(); 141 | return time() - $this->lastNetworkActivity; 142 | } 143 | 144 | /** 145 | * @throws TransportException 146 | */ 147 | private function assertConnected() 148 | { 149 | if (!$this->socket) { 150 | throw new TransportException('Connection not initialized', TransportException::NOT_INITIALIZED); 151 | } 152 | } 153 | 154 | /** 155 | * @param string $method 156 | * @param mixed[] $args 157 | */ 158 | function addCall($method, $args) 159 | { 160 | $this->multicallBuffer[] = [ 161 | 'methodName' => $method, 162 | 'params' => $args 163 | ]; 164 | } 165 | 166 | /** 167 | * @return mixed 168 | */ 169 | function multiquery() 170 | { 171 | switch (count($this->multicallBuffer)) { 172 | case 0: 173 | return []; 174 | case 1: 175 | $call = array_shift($this->multicallBuffer); 176 | return [$this->query($call['methodName'], $call['params'])]; 177 | default: 178 | $result = $this->query('system.multicall', [$this->multicallBuffer]); 179 | foreach ($result as &$value) { 180 | if (isset($value['faultCode'])) { 181 | $value = FaultException::create($value['faultString'], $value['faultCode']); 182 | } else { 183 | $value = $value[0]; 184 | } 185 | } 186 | $this->multicallBuffer = []; 187 | return $result; 188 | } 189 | } 190 | 191 | /** 192 | * @param string $method 193 | * @param mixed[] $args 194 | * @return mixed 195 | * @throws MessageException 196 | */ 197 | function query($method, $args = []) 198 | { 199 | $this->assertConnected(); 200 | $xml = Request::encode($method, $args); 201 | 202 | if (strlen($xml) > self::MAX_REQUEST_SIZE - 8) { 203 | if ($method != 'system.multicall' || count($args[0]) < 2) { 204 | throw new MessageException('Request too large', MessageException::REQUEST_TOO_LARGE); 205 | } 206 | 207 | $mid = count($args[0]) >> 1; 208 | $res1 = $this->query('system.multicall', [array_slice($args[0], 0, $mid)]); 209 | $res2 = $this->query('system.multicall', [array_slice($args[0], $mid)]); 210 | return array_merge($res1, $res2); 211 | } 212 | 213 | $this->writeMessage($xml); 214 | return $this->flush(true); 215 | } 216 | 217 | /** 218 | * @param string $xml 219 | * @throws TransportException 220 | */ 221 | private function writeMessage($xml) 222 | { 223 | if ($this->requestHandle == (int)0xffffffff) { 224 | $this->requestHandle = (int)0x80000000; 225 | } 226 | $data = pack('V2', strlen($xml), ++$this->requestHandle) . $xml; 227 | if (!$this->write($data)) { 228 | $this->onIoFailure('while writing'); 229 | } 230 | $this->lastNetworkActivity = time(); 231 | } 232 | 233 | /** 234 | * @param string $data 235 | * @return boolean 236 | */ 237 | private function write($data) 238 | { 239 | @stream_set_timeout($this->socket, $this->writeTimeout['sec'], $this->writeTimeout['usec']); 240 | self::$sent += strlen($data); 241 | 242 | while (strlen($data) > 0) { 243 | $written = @fwrite($this->socket, $data); 244 | if ($written === 0 || $written === false) { 245 | return false; 246 | } 247 | 248 | $data = substr($data, $written); 249 | } 250 | 251 | return true; 252 | } 253 | 254 | /** 255 | * @param bool $waitResponse 256 | * @return mixed 257 | * @throws FaultException 258 | */ 259 | private function flush($waitResponse = false) 260 | { 261 | $r = [$this->socket]; 262 | while ($waitResponse || @stream_select($r, $w, $e, 0) > 0) { 263 | list($handle, $xml) = $this->readMessage(); 264 | list($type, $value) = Request::decode($xml); 265 | switch ($type) { 266 | case 'fault': 267 | throw FaultException::create($value['faultString'], $value['faultCode']); 268 | case 'response': 269 | if ($handle == $this->requestHandle) { 270 | return $value; 271 | } 272 | break; 273 | case 'call': 274 | $this->callbacksBuffer[] = $value; 275 | } 276 | } 277 | } 278 | 279 | /** 280 | * @return mixed[] 281 | * @throws TransportException 282 | * @throws MessageException 283 | */ 284 | private function readMessage() 285 | { 286 | $header = $this->read(8); 287 | if ($header === false) { 288 | $this->onIoFailure('while reading header'); 289 | } 290 | 291 | extract(unpack('Vsize/Vhandle', $header)); 292 | /** @var $size int */ 293 | /** @var $handle int */ 294 | if ($size == 0 || $handle == 0) { 295 | throw new TransportException('Incorrect header', TransportException::PROTOCOL_ERROR); 296 | } 297 | 298 | if ($size > self::MAX_RESPONSE_SIZE) { 299 | throw new MessageException('Response too large', MessageException::RESPONSE_TOO_LARGE); 300 | } 301 | 302 | $data = $this->read($size); 303 | if ($data === false) { 304 | $this->onIoFailure('while reading data'); 305 | } 306 | 307 | $this->lastNetworkActivity = time(); 308 | return [$handle, $data]; 309 | } 310 | 311 | /** 312 | * @return mixed[] 313 | */ 314 | function getCallbacks() 315 | { 316 | $this->assertConnected(); 317 | $this->flush(); 318 | $cb = $this->callbacksBuffer; 319 | $this->callbacksBuffer = []; 320 | return $cb; 321 | } 322 | } 323 | 324 | class TransportException extends Exception 325 | { 326 | const NOT_INITIALIZED = 1; 327 | const INTERRUPTED = 2; 328 | const TIMED_OUT = 3; 329 | const WRONG_PROTOCOL = 4; 330 | const PROTOCOL_ERROR = 5; 331 | } 332 | 333 | class MessageException extends Exception 334 | { 335 | const REQUEST_TOO_LARGE = 1; 336 | const RESPONSE_TOO_LARGE = 2; 337 | } 338 | -------------------------------------------------------------------------------- /src/Maniaplanet/DedicatedServer/Connection.php: -------------------------------------------------------------------------------- 1 | -1, 28 | 'User' => 0, 29 | 'Admin' => 1, 30 | 'SuperAdmin' => 2 31 | ]; 32 | /** @var Xmlrpc\GbxRemote */ 33 | protected $xmlrpcClient; 34 | /** @var string */ 35 | protected $user; 36 | /** @var callable[] */ 37 | private $multicallHandlers = []; 38 | 39 | public function __construct( 40 | $host = '127.0.0.1', 41 | $port = 5000, 42 | $timeout = 5, 43 | $user = 'SuperAdmin', 44 | $password = 'SuperAdmin', 45 | $apiVersion = self::API_2013_04_16 46 | ) { 47 | $this->xmlrpcClient = new Xmlrpc\GbxRemote($host, $port, $timeout); 48 | $this->authenticate($user, $password); 49 | $this->setApiVersion($apiVersion); 50 | } 51 | 52 | /** 53 | * Allow user authentication by specifying a login and a password, to gain access to the set of functionalities corresponding to this authorization level. 54 | * @param string $user 55 | * @param string $password 56 | * @return bool 57 | * @throws InvalidArgumentException 58 | */ 59 | public function authenticate($user, $password) 60 | { 61 | if (!is_string($user) || !isset(self::$levels[$user])) { 62 | throw new InvalidArgumentException('user = ' . print_r($user, true)); 63 | } 64 | if (self::$levels[$this->user] >= self::$levels[$user]) { 65 | return true; 66 | } 67 | 68 | if (!is_string($password)) { 69 | throw new InvalidArgumentException('password = ' . print_r($password, true)); 70 | } 71 | 72 | $res = $this->execute(ucfirst(__FUNCTION__), [$user, $password]); 73 | if ($res) { 74 | $this->user = $user; 75 | } 76 | return $res; 77 | } 78 | 79 | /** 80 | * Add a call in queue. It will be executed by the next Call from the user to executeMulticall 81 | * @param string $methodName 82 | * @param mixed[] $params 83 | * @param bool|callable $multicall True to queue the request or false to execute it immediately 84 | * @return mixed 85 | */ 86 | public function execute($methodName, $params = [], $multicall = false) 87 | { 88 | if ($multicall) { 89 | $this->xmlrpcClient->addCall($methodName, $params); 90 | $this->multicallHandlers[] = $multicall; 91 | } else { 92 | return $this->xmlrpcClient->query($methodName, $params); 93 | } 94 | } 95 | 96 | /** 97 | * Define the wanted api. 98 | * @param string $version 99 | * @param bool $multicall 100 | * @return bool 101 | */ 102 | public function setApiVersion(string $version, $multicall = false) 103 | { 104 | return $this->execute(ucfirst(__FUNCTION__), [$version], $multicall); 105 | } 106 | 107 | /** 108 | * Close the current socket connexion 109 | * Never call this method, use instead DedicatedApi::delete($host, $port) 110 | */ 111 | public function terminate() 112 | { 113 | $this->xmlrpcClient->terminate(); 114 | } 115 | 116 | /** 117 | * Change client timeouts 118 | * @param int $read read timeout (in ms), 0 to leave unchanged 119 | * @param int $write write timeout (in ms), 0 to leave unchanged 120 | */ 121 | public function setTimeouts($read = null, $write = null) 122 | { 123 | $this->xmlrpcClient->setTimeouts($read, $write); 124 | } 125 | 126 | /** 127 | * @return int Network idle time in seconds 128 | */ 129 | public function getIdleTime() 130 | { 131 | return $this->xmlrpcClient->getIdleTime(); 132 | } 133 | 134 | /** 135 | * Return pending callbacks 136 | * @return mixed[] 137 | */ 138 | public function executeCallbacks() 139 | { 140 | return $this->xmlrpcClient->getCallbacks(); 141 | } 142 | 143 | /** 144 | * Execute the calls in queue and return the result 145 | * @return mixed[] 146 | */ 147 | public function executeMulticall() 148 | { 149 | $responses = $this->xmlrpcClient->multiquery(); 150 | foreach ($responses as $i => &$response) { 151 | if (!($response instanceof Xmlrpc\FaultException) && is_callable($this->multicallHandlers[$i])) { 152 | $response = call_user_func($this->multicallHandlers[$i], $response); 153 | } 154 | } 155 | $this->multicallHandlers = []; 156 | return $responses; 157 | } 158 | 159 | /** 160 | * Change the password for the specified login/user. 161 | * Only available to SuperAdmin. 162 | * @param string $user 163 | * @param string $password 164 | * @param bool $multicall 165 | * @return bool 166 | * @throws InvalidArgumentException 167 | */ 168 | public function changeAuthPassword(string $user, string $password, $multicall = false) 169 | { 170 | if (!isset(self::$levels[$user])) { 171 | throw new InvalidArgumentException('user = ' . print_r($user, true)); 172 | } 173 | 174 | return $this->execute(ucfirst(__FUNCTION__), [$user, $password], $multicall); 175 | } 176 | 177 | /** 178 | * Allow the GameServer to call you back. 179 | * @param bool $enable 180 | * @param bool $multicall 181 | * @return bool 182 | */ 183 | public function enableCallbacks(bool $enable = true, $multicall = false) 184 | { 185 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 186 | } 187 | 188 | /** 189 | * Returns a struct with the Name, TitleId, Version, Build and ApiVersion of the application remotely controlled. 190 | * @param bool $multicall 191 | * @return Structures\Version 192 | */ 193 | public function getVersion($multicall = false) 194 | { 195 | if ($multicall) { 196 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Version')); 197 | } 198 | return Structures\Version::fromArray($this->execute(ucfirst(__FUNCTION__))); 199 | } 200 | 201 | /** 202 | * @param string $struct 203 | * @param bool $array 204 | * @return callable 205 | */ 206 | private function structHandler($struct, $array = false) 207 | { 208 | return ['\\' . __NAMESPACE__ . '\Structures\\' . $struct, 'fromArray' . ($array ? 'OfArray' : '')]; 209 | } 210 | 211 | /** 212 | * Returns the current status of the server. 213 | * @param bool $multicall 214 | * @return Structures\Status 215 | */ 216 | public function getStatus($multicall = false) 217 | { 218 | if ($multicall) { 219 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Status')); 220 | } 221 | return Structures\Status::fromArray($this->execute(ucfirst(__FUNCTION__))); 222 | } 223 | 224 | /** 225 | * Quit the application. 226 | * Only available to SuperAdmin. 227 | * @param bool $multicall 228 | * @return bool 229 | */ 230 | public function quitGame($multicall = false) 231 | { 232 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 233 | } 234 | 235 | /** 236 | * Call a vote to kick a player. 237 | * You can additionally supply specific parameters for this vote: a ratio, a time out and who is voting. 238 | * Only available to Admin. 239 | * @param mixed $player A player object or a login 240 | * @param float $ratio In range [0,1] or -1 for default ratio 241 | * @param int $timeout In milliseconds, 0 for default timeout, 1 for indefinite 242 | * @param int $voters 0: active players, 1: any player, 2: everybody including pure spectators 243 | * @param bool $multicall 244 | * @return bool 245 | * @throws InvalidArgumentException 246 | */ 247 | public function callVoteKick($player, $ratio = 0.5, $timeout = 0, $voters = 1, $multicall = false) 248 | { 249 | $login = $this->getLogin($player); 250 | if ($login === false) { 251 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 252 | } 253 | 254 | $vote = new Structures\Vote(Structures\VoteRatio::COMMAND_KICK, [$login]); 255 | return $this->callVote($vote, $ratio, $timeout, $voters, $multicall); 256 | } 257 | 258 | /** 259 | * Returns the login of the given player 260 | * @param mixed $player 261 | * @return string|bool 262 | */ 263 | private function getLogin($player, $allowEmpty = false) 264 | { 265 | if (is_object($player)) { 266 | if (property_exists($player, 'login')) { 267 | $player = $player->login; 268 | } else { 269 | return false; 270 | } 271 | } 272 | if (empty($player)) { 273 | return $allowEmpty ? '' : false; 274 | } 275 | if (is_string($player)) { 276 | return $player; 277 | } 278 | return false; 279 | } 280 | 281 | /** 282 | * Call a vote for a command. 283 | * You can additionally supply specific parameters for this vote: a ratio, a time out and who is voting. 284 | * Only available to Admin. 285 | * @param Structures\Vote $vote 286 | * @param float $ratio In range [0,1] or -1 for default ratio 287 | * @param int $timeout In milliseconds, 0 for default timeout, 1 for indefinite 288 | * @param int $voters 0: active players, 1: any player, 2: everybody including pure spectators 289 | * @param bool $multicall 290 | * @return bool 291 | * @throws InvalidArgumentException 292 | */ 293 | public function callVote($vote, $ratio = -1., $timeout = 0, $voters = 1, $multicall = false) 294 | { 295 | if (!($vote instanceof Structures\Vote && $vote->isValid())) { 296 | throw new InvalidArgumentException('vote = ' . print_r($vote, true)); 297 | } 298 | if (!Structures\VoteRatio::isRatio($ratio)) { 299 | throw new InvalidArgumentException('ratio = ' . print_r($ratio, true)); 300 | } 301 | if (!is_int($timeout)) { 302 | throw new InvalidArgumentException('timeout = ' . print_r($timeout, true)); 303 | } 304 | if (!is_int($voters) || $voters < 0 || $voters > 2) { 305 | throw new InvalidArgumentException('voters = ' . print_r($voters, true)); 306 | } 307 | 308 | $xml = Xmlrpc\Request::encode($vote->cmdName, $vote->cmdParam, false); 309 | return $this->execute(ucfirst(__FUNCTION__) . 'Ex', [$xml, $ratio, $timeout, $voters], $multicall); 310 | } 311 | 312 | /** 313 | * Call a vote to ban a player. 314 | * You can additionally supply specific parameters for this vote: a ratio, a time out and who is voting. 315 | * Only available to Admin. 316 | * @param mixed $player A player object or a login 317 | * @param float $ratio In range [0,1] or -1 for default ratio 318 | * @param int $timeout In milliseconds, 0 for default timeout, 1 for indefinite 319 | * @param int $voters 0: active players, 1: any player, 2: everybody including pure spectators 320 | * @param bool $multicall 321 | * @return bool 322 | * @throws InvalidArgumentException 323 | */ 324 | public function callVoteBan($player, $ratio = 0.6, $timeout = 0, $voters = 1, $multicall = false) 325 | { 326 | $login = $this->getLogin($player); 327 | if ($login === false) { 328 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 329 | } 330 | 331 | $vote = new Structures\Vote(Structures\VoteRatio::COMMAND_BAN, [$login]); 332 | return $this->callVote($vote, $ratio, $timeout, $voters, $multicall); 333 | } 334 | 335 | /** 336 | * Call a vote to restart the current map. 337 | * You can additionally supply specific parameters for this vote: a ratio, a time out and who is voting. 338 | * Only available to Admin. 339 | * @param float $ratio In range [0,1] or -1 for default ratio 340 | * @param int $timeout In milliseconds, 0 for default timeout, 1 for indefinite 341 | * @param int $voters 0: active players, 1: any player, 2: everybody including pure spectators 342 | * @param bool $multicall 343 | * @return bool 344 | * @throws InvalidArgumentException 345 | */ 346 | public function callVoteRestartMap($ratio = 0.5, $timeout = 0, $voters = 1, $multicall = false) 347 | { 348 | $vote = new Structures\Vote(Structures\VoteRatio::COMMAND_RESTART_MAP); 349 | return $this->callVote($vote, $ratio, $timeout, $voters, $multicall); 350 | } 351 | 352 | /** 353 | * Call a vote to go to the next map. 354 | * You can additionally supply specific parameters for this vote: a ratio, a time out and who is voting. 355 | * Only available to Admin. 356 | * @param float $ratio In range [0,1] or -1 for default ratio 357 | * @param int $timeout In milliseconds, 0 for default timeout, 1 for indefinite 358 | * @param int $voters 0: active players, 1: any player, 2: everybody including pure spectators 359 | * @param bool $multicall 360 | * @return bool 361 | * @throws InvalidArgumentException 362 | */ 363 | public function callVoteNextMap($ratio = 0.5, $timeout = 0, $voters = 1, $multicall = false) 364 | { 365 | $vote = new Structures\Vote(Structures\VoteRatio::COMMAND_NEXT_MAP); 366 | return $this->callVote($vote, $ratio, $timeout, $voters, $multicall); 367 | } 368 | 369 | /** 370 | * Cancel the current vote. 371 | * Only available to Admin. 372 | * @param bool $multicall 373 | * @return bool 374 | */ 375 | public function cancelVote($multicall = false) 376 | { 377 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 378 | } 379 | 380 | /** 381 | * Returns the vote currently in progress. 382 | * @param $multicall 383 | * @return Structures\Vote 384 | */ 385 | public function getCurrentCallVote($multicall = false) 386 | { 387 | if ($multicall) { 388 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Vote')); 389 | } 390 | return Structures\Vote::fromArray($this->execute(ucfirst(__FUNCTION__))); 391 | } 392 | 393 | /** 394 | * Set a new timeout for waiting for votes. 395 | * Only available to Admin. 396 | * @param int $timeout In milliseconds, 0 to disable votes 397 | * @param bool $multicall 398 | * @return bool 399 | */ 400 | public function setCallVoteTimeOut(int $timeout, $multicall = false) 401 | { 402 | return $this->execute(ucfirst(__FUNCTION__), [$timeout], $multicall); 403 | } 404 | 405 | /** 406 | * Get the current and next timeout for waiting for votes. 407 | * @param $multicall 408 | * @return int[] {int CurrentValue, int NextValue} 409 | */ 410 | public function getCallVoteTimeOut($multicall = false) 411 | { 412 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 413 | } 414 | 415 | /** 416 | * Set a new default ratio for passing a vote. 417 | * Only available to Admin. 418 | * @param float $ratio In range [0,1] or -1 to disable votes 419 | * @param bool $multicall 420 | * @return bool 421 | * @throws InvalidArgumentException 422 | */ 423 | public function setCallVoteRatio($ratio, $multicall = false) 424 | { 425 | if (!Structures\VoteRatio::isRatio($ratio)) { 426 | throw new InvalidArgumentException('ratio = ' . print_r($ratio, true)); 427 | } 428 | 429 | return $this->execute(ucfirst(__FUNCTION__), [$ratio], $multicall); 430 | } 431 | 432 | /** 433 | * Get the current default ratio for passing a vote. 434 | * @param bool $multicall 435 | * @return float 436 | */ 437 | public function getCallVoteRatio($multicall = false) 438 | { 439 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 440 | } 441 | 442 | /** 443 | * Set the ratios list for passing specific votes, extended version with parameters matching. 444 | * Only available to Admin. 445 | * @param Structures\VoteRatio[] $ratios 446 | * @param bool $replaceAll True to override the whole ratios list or false to modify only specified ratios 447 | * @param bool $multicall 448 | * @return bool 449 | * @throws InvalidArgumentException 450 | */ 451 | public function setCallVoteRatios(array $ratios, bool $replaceAll = true, $multicall = false) 452 | { 453 | foreach ($ratios as $i => &$ratio) { 454 | if (!($ratio instanceof Structures\VoteRatio && $ratio->isValid())) { 455 | throw new InvalidArgumentException('ratios[' . $i . '] = ' . print_r($ratios, true)); 456 | } 457 | $ratio = $ratio->toArray(); 458 | } 459 | 460 | return $this->execute(ucfirst(__FUNCTION__) . 'Ex', [$replaceAll, $ratios], $multicall); 461 | } 462 | 463 | /** 464 | * Get the current ratios for passing votes, extended version with parameters matching. 465 | * @param bool $multicall 466 | * @return Structures\VoteRatio[] 467 | */ 468 | public function getCallVoteRatios($multicall = false) 469 | { 470 | if ($multicall) { 471 | return $this->execute(ucfirst(__FUNCTION__) . 'Ex', [], $this->structHandler('VoteRatio', true)); 472 | } 473 | return Structures\VoteRatio::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__) . 'Ex')); 474 | } 475 | 476 | /** 477 | * Send a text message, possibly localised to a specific login or to everyone, without the server login. 478 | * Only available to Admin. 479 | * @param string|string[][] $message Single string or array of structures {Lang='xx', Text='...'}: 480 | * if no matching language is found, the last text in the array is used 481 | * @param mixed $recipient Login, player object or array; null for all 482 | * @param bool $multicall 483 | * @return bool 484 | * @throws InvalidArgumentException 485 | */ 486 | public function chatSendServerMessage($message, $recipient = null, $multicall = false) 487 | { 488 | $logins = $this->getLogins($recipient, true); 489 | if ($logins === false) { 490 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 491 | } 492 | 493 | if (is_array($message)) { 494 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLanguage', [$message, $logins], $multicall); 495 | } 496 | if (is_string($message)) { 497 | if ($logins) { 498 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$message, $logins], $multicall); 499 | } 500 | return $this->execute(ucfirst(__FUNCTION__), [$message], $multicall); 501 | } 502 | // else 503 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 504 | } 505 | 506 | /** 507 | * Returns logins of given players 508 | * @param mixed $players 509 | * @return string|bool 510 | */ 511 | private function getLogins($players, $allowEmpty = false) 512 | { 513 | if (is_array($players)) { 514 | $logins = []; 515 | foreach ($players as $player) { 516 | $login = $this->getLogin($player); 517 | if ($login === false) { 518 | return false; 519 | } 520 | $logins[] = $login; 521 | } 522 | 523 | return implode(',', $logins); 524 | } 525 | return $this->getLogin($players, $allowEmpty); 526 | } 527 | 528 | /** 529 | * Send a text message, possibly localised to a specific login or to everyone. 530 | * Only available to Admin. 531 | * @param string|string[][] $message Single string or array of structures {Lang='xx', Text='...'}: 532 | * if no matching language is found, the last text in the array is used 533 | * @param mixed $recipient Login, player object or array; null for all 534 | * @param bool $multicall 535 | * @return bool 536 | * @throws InvalidArgumentException 537 | */ 538 | public function chatSend($message, $recipient = null, $multicall = false) 539 | { 540 | $logins = $this->getLogins($recipient, true); 541 | if ($logins === false) { 542 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 543 | } 544 | 545 | if (is_array($message)) { 546 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLanguage', [$message, $logins], $multicall); 547 | } 548 | if (is_string($message)) { 549 | if ($logins) { 550 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$message, $logins], $multicall); 551 | } 552 | return $this->execute(ucfirst(__FUNCTION__), [$message], $multicall); 553 | } 554 | // else 555 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 556 | } 557 | 558 | /** 559 | * Returns the last chat lines. Maximum of 40 lines. 560 | * Only available to Admin. 561 | * @param bool $multicall 562 | * @return string[] 563 | */ 564 | public function getChatLines($multicall = false) 565 | { 566 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 567 | } 568 | 569 | /** 570 | * The chat messages are no longer dispatched to the players, they only go to the rpc callback and the controller has to manually forward them. 571 | * Only available to Admin. 572 | * @param bool $enable 573 | * @param bool $excludeServer Allows all messages from the server to be automatically forwarded. 574 | * @param bool $multicall 575 | * @return bool 576 | * @throws InvalidArgumentException 577 | */ 578 | public function chatEnableManualRouting(bool $enable = true, bool $excludeServer = false, $multicall = false) 579 | { 580 | return $this->execute(ucfirst(__FUNCTION__), [$enable, $excludeServer], $multicall); 581 | } 582 | 583 | /** 584 | * Send a message to the specified recipient (or everybody if empty) on behalf of sender. 585 | * Only available if manual routing is enabled. 586 | * Only available to Admin. 587 | * @param string $message 588 | * @param mixed $sender Login or player object 589 | * @param mixed $recipient Login, player object or array; empty for all 590 | * @param bool $multicall 591 | * @return bool 592 | * @throws InvalidArgumentException 593 | */ 594 | public function chatForward(string $message, $sender, $recipient = null, $multicall = false) 595 | { 596 | $senderLogin = $this->getLogin($sender); 597 | if ($senderLogin === false) { 598 | throw new InvalidArgumentException('sender = ' . print_r($sender, true)); 599 | } 600 | $recipientLogins = $this->getLogins($recipient, true); 601 | if ($recipientLogins === false) { 602 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 603 | } 604 | 605 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$message, $senderLogin, $recipientLogins], $multicall); 606 | } 607 | 608 | /** 609 | * Display a notice on all clients. 610 | * Only available to Admin. 611 | * @param mixed $recipient Login, player object or array; empty for all 612 | * @param string $message 613 | * @param mixed $avatar Login or player object whose avatar will be displayed; empty for none 614 | * @param int $variant 0: normal, 1: sad, 2: happy 615 | * @param bool $multicall 616 | * @return bool 617 | * @throws InvalidArgumentException 618 | */ 619 | public function sendNotice($recipient, $message, $avatar = null, $variant = 0, $multicall = false) 620 | { 621 | $logins = $this->getLogins($recipient, true); 622 | if ($logins === false) { 623 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 624 | } 625 | if (!is_string($message)) { 626 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 627 | } 628 | $avatarLogin = $this->getLogin($avatar, true); 629 | if ($avatarLogin === false) { 630 | throw new InvalidArgumentException('avatar = ' . print_r($avatar, true)); 631 | } 632 | if (!is_int($variant) || $variant < 0 || $variant > 2) { 633 | throw new InvalidArgumentException('variant = ' . print_r($variant, true)); 634 | } 635 | 636 | if ($logins) { 637 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$logins, $message, $avatarLogin, $variant], $multicall); 638 | } 639 | return $this->execute(ucfirst(__FUNCTION__), [$message, $avatar, $variant], $multicall); 640 | } 641 | 642 | /** 643 | * Display a manialink page on all clients. 644 | * Only available to Admin. 645 | * @param mixed $recipient Login, player object or array; empty for all 646 | * @param string $manialinks XML string 647 | * @param int $timeout Seconds before autohide, 0 for permanent 648 | * @param bool $hideOnClick Hide as soon as the user clicks on a page option 649 | * @param bool $multicall 650 | * @return bool 651 | * @throws InvalidArgumentException 652 | */ 653 | public function sendDisplayManialinkPage($recipient, string $manialinks, int $timeout = 0, bool $hideOnClick = false, $multicall = false) 654 | { 655 | $logins = $this->getLogins($recipient, true); 656 | if ($logins === false) { 657 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 658 | } 659 | 660 | if ($logins) { 661 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$logins, $manialinks, $timeout, $hideOnClick], $multicall); 662 | } 663 | return $this->execute(ucfirst(__FUNCTION__), [$manialinks, $timeout, $hideOnClick], $multicall); 664 | } 665 | 666 | /** 667 | * Hide the displayed manialink page. 668 | * Only available to Admin. 669 | * @param mixed $recipient Login, player object or array; empty for all 670 | * @param bool $multicall 671 | * @return bool 672 | */ 673 | public function sendHideManialinkPage($recipient = null, $multicall = false) 674 | { 675 | $logins = $this->getLogins($recipient, true); 676 | if ($logins === false) { 677 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 678 | } 679 | 680 | if ($logins) { 681 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$logins], $multicall); 682 | } 683 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 684 | } 685 | 686 | /** 687 | * Returns the latest results from the current manialink page as an array of structs {string Login, int PlayerId, int Result}: 688 | * - Result == 0 -> no answer 689 | * - Result > 0 -> answer from the player. 690 | * @param bool $multicall 691 | * @return Structures\PlayerAnswer[] 692 | */ 693 | public function getManialinkPageAnswers($multicall = false) 694 | { 695 | if ($multicall) { 696 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('PlayerAnswer', true)); 697 | } 698 | return Structures\PlayerAnswer::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__))); 699 | } 700 | 701 | /** 702 | * Opens a link in the client with the specified login. 703 | * Only available to Admin. 704 | * @param mixed $recipient Login, player object or array 705 | * @param string $link URL to open 706 | * @param int $linkType 0: in the external browser, 1: in the internal manialink browser 707 | * @param bool $multicall 708 | * @return bool 709 | * @throws InvalidArgumentException 710 | */ 711 | public function sendOpenLink($recipient, string $link, $linkType, $multicall = false) 712 | { 713 | $logins = $this->getLogins($recipient); 714 | if ($logins === false) { 715 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 716 | } 717 | if (!is_string($link)) { 718 | throw new InvalidArgumentException('link = ' . print_r($link, true)); 719 | } 720 | if ($linkType !== 0 && $linkType !== 1) { 721 | throw new InvalidArgumentException('linkType = ' . print_r($linkType, true)); 722 | } 723 | 724 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$logins, $link, $linkType], $multicall); 725 | } 726 | 727 | /** 728 | * Prior to loading next map, execute SendToServer url '#qjoin=login@title' 729 | * Only available to Admin. 730 | * Available since ManiaPlanet 4 731 | * @param $link 732 | * @param bool $multicall 733 | * @return bool 734 | */ 735 | public function sendToServerAfterMatchEnd(string $link, $multicall = false) 736 | { 737 | $link = str_replace("maniaplanet://", "", $link); 738 | 739 | return $this->execute(ucfirst(__FUNCTION__), [$link], $multicall); 740 | } 741 | 742 | /** 743 | * Kick the player with the specified login, with an optional message. 744 | * Only available to Admin. 745 | * @param mixed $player Login or player object 746 | * @param string $message 747 | * @param bool $multicall 748 | * @return bool 749 | * @throws InvalidArgumentException 750 | */ 751 | public function kick($player, $message = '', $multicall = false) 752 | { 753 | $login = $this->getLogin($player); 754 | if ($login === false) { 755 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 756 | } 757 | if (!is_string($message)) { 758 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 759 | } 760 | 761 | return $this->execute(ucfirst(__FUNCTION__), [$login, $message], $multicall); 762 | } 763 | 764 | /** 765 | * Ban the player with the specified login, with an optional message. 766 | * Only available to Admin. 767 | * @param mixed $player Login or player object 768 | * @param string $message 769 | * @param bool $multicall 770 | * @return bool 771 | * @throws InvalidArgumentException 772 | */ 773 | public function ban($player, $message = '', $multicall = false) 774 | { 775 | $login = $this->getLogin($player); 776 | if ($login === false) { 777 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 778 | } 779 | if (!is_string($message)) { 780 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 781 | } 782 | 783 | return $this->execute(ucfirst(__FUNCTION__), [$login, $message], $multicall); 784 | } 785 | 786 | /** 787 | * Ban the player with the specified login, with a message. 788 | * Add it to the black list, and optionally save the new list. 789 | * Only available to Admin. 790 | * @param mixed $player Login or player object 791 | * @param string $message 792 | * @param bool $save 793 | * @param bool $multicall 794 | * @return bool 795 | * @throws InvalidArgumentException 796 | */ 797 | public function banAndBlackList($player, string $message = '', bool $save = false, $multicall = false) 798 | { 799 | $login = $this->getLogin($player); 800 | if ($login === false) { 801 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 802 | } 803 | 804 | return $this->execute(ucfirst(__FUNCTION__), [$player, $message, $save], $multicall); 805 | } 806 | 807 | /** 808 | * Unban the player with the specified login. 809 | * Only available to Admin. 810 | * @param mixed $player Login or player object 811 | * @param bool $multicall 812 | * @return bool 813 | * @throws InvalidArgumentException 814 | */ 815 | public function unBan($player, $multicall = false) 816 | { 817 | $login = $this->getLogin($player); 818 | if ($login === false) { 819 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 820 | } 821 | 822 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 823 | } 824 | 825 | /** 826 | * Clean the ban list of the server. 827 | * Only available to Admin. 828 | * @param bool $multicall 829 | * @return bool 830 | */ 831 | public function cleanBanList($multicall = false) 832 | { 833 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 834 | } 835 | 836 | /** 837 | * Returns the list of banned players. 838 | * @param int $length Maximum number of infos to be returned 839 | * @param int $offset Starting index in the list 840 | * @param bool $multicall 841 | * @return Structures\PlayerBan[] 842 | */ 843 | public function getBanList(int $length = -1, int $offset = 0, $multicall = false) 844 | { 845 | if ($multicall) { 846 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('PlayerBan', true)); 847 | } 848 | return Structures\PlayerBan::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 849 | } 850 | 851 | /** 852 | * Blacklist the player with the specified login. 853 | * Only available to SuperAdmin. 854 | * @param mixed $player Login or player object 855 | * @param bool $multicall 856 | * @return bool 857 | * @throws InvalidArgumentException 858 | */ 859 | public function blackList($player, $multicall = false) 860 | { 861 | $login = $this->getLogin($player); 862 | if ($login === false) { 863 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 864 | } 865 | 866 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 867 | } 868 | 869 | /** 870 | * UnBlackList the player with the specified login. 871 | * Only available to SuperAdmin. 872 | * @param mixed $player Login or player object 873 | * @param bool $multicall 874 | * @return bool 875 | * @throws InvalidArgumentException 876 | */ 877 | public function unBlackList($player, $multicall = false) 878 | { 879 | $login = $this->getLogin($player); 880 | if ($login === false) { 881 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 882 | } 883 | 884 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 885 | } 886 | 887 | /** 888 | * Clean the blacklist of the server. 889 | * Only available to SuperAdmin. 890 | * @param bool $multicall 891 | * @return bool 892 | */ 893 | public function cleanBlackList($multicall = false) 894 | { 895 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 896 | } 897 | 898 | /** 899 | * Returns the list of blacklisted players. 900 | * @param int $length Maximum number of infos to be returned 901 | * @param int $offset Starting index in the list 902 | * @param bool $multicall 903 | * @return Structures\Player[] 904 | */ 905 | public function getBlackList(int $length = -1, int $offset = 0, $multicall = false) 906 | { 907 | if ($multicall) { 908 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('Player', true)); 909 | } 910 | return Structures\Player::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 911 | } 912 | 913 | /** 914 | * Load the black list file with the specified file name. 915 | * Only available to Admin. 916 | * @param string $filename Empty for default filename (blacklist.txt) 917 | * @param bool $multicall 918 | * @return bool 919 | * @throws InvalidArgumentException 920 | */ 921 | public function loadBlackList(string $filename = '', $multicall = false) 922 | { 923 | $filename = $this->secureUtf8($filename); 924 | 925 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 926 | } 927 | 928 | /** 929 | * @param string|string[] $filename 930 | * @return string|string[] 931 | */ 932 | private function secureUtf8($filename) 933 | { 934 | if (is_string($filename)) { 935 | $filename = $this->stripBom($filename); 936 | if (preg_match('/[^\x09\x0A\x0D\x20-\x7E]/', $filename)) { 937 | return "\xEF\xBB\xBF" . $filename; 938 | } 939 | return $filename; 940 | } 941 | return array_map([$this, 'secureUtf8'], $filename); 942 | } 943 | 944 | /** 945 | * @param string|string[] $str 946 | * @return string|string[] 947 | */ 948 | private function stripBom($str) 949 | { 950 | if (is_string($str)) { 951 | return str_replace("\xEF\xBB\xBF", '', $str); 952 | } 953 | return array_map([$this, 'stripBom'], $str); 954 | } 955 | 956 | /** 957 | * Save the black list in the file with specified file name. 958 | * Only available to Admin. 959 | * @param string $filename Empty for default filename (blacklist.txt) 960 | * @param bool $multicall 961 | * @return bool 962 | * @throws InvalidArgumentException 963 | */ 964 | public function saveBlackList(string $filename = '', $multicall = false) 965 | { 966 | $filename = $this->secureUtf8($filename); 967 | 968 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 969 | } 970 | 971 | /** 972 | * Add the player with the specified login on the guest list. 973 | * Only available to Admin. 974 | * @param mixed $player Login or player object 975 | * @param bool $multicall 976 | * @return bool 977 | * @throws InvalidArgumentException 978 | */ 979 | public function addGuest($player, $multicall = false) 980 | { 981 | $login = $this->getLogin($player); 982 | if ($login === false) { 983 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 984 | } 985 | 986 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 987 | } 988 | 989 | /** 990 | * Remove the player with the specified login from the guest list. 991 | * Only available to Admin. 992 | * @param mixed $player Login or player object 993 | * @param bool $multicall 994 | * @return bool 995 | * @throws InvalidArgumentException 996 | */ 997 | public function removeGuest($player, $multicall = false) 998 | { 999 | $login = $this->getLogin($player); 1000 | if ($login === false) { 1001 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1002 | } 1003 | 1004 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 1005 | } 1006 | 1007 | /** 1008 | * Clean the guest list of the server. 1009 | * Only available to Admin. 1010 | * @param bool $multicall 1011 | * @return bool 1012 | */ 1013 | public function cleanGuestList($multicall = false) 1014 | { 1015 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1016 | } 1017 | 1018 | /** 1019 | * Returns the list of players on the guest list. 1020 | * @param int $length Maximum number of infos to be returned 1021 | * @param int $offset Starting index in the list 1022 | * @param bool $multicall 1023 | * @return Structures\Player[] 1024 | */ 1025 | public function getGuestList(int $length = -1, int $offset = 0, $multicall = false) 1026 | { 1027 | if ($multicall) { 1028 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('Player', true)); 1029 | } 1030 | return Structures\Player::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 1031 | } 1032 | 1033 | /** 1034 | * Load the guest list file with the specified file name. 1035 | * Only available to Admin. 1036 | * @param string $filename Empty for default filename (guestlist.txt) 1037 | * @param bool $multicall 1038 | * @return bool 1039 | * @throws InvalidArgumentException 1040 | */ 1041 | public function loadGuestList(string $filename = '', $multicall = false) 1042 | { 1043 | $filename = $this->secureUtf8($filename); 1044 | 1045 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 1046 | } 1047 | 1048 | /** 1049 | * Save the guest list in the file with specified file name. 1050 | * Only available to Admin. 1051 | * @param string $filename Empty for default filename (guestlist.txt) 1052 | * @param bool $multicall 1053 | * @return bool 1054 | */ 1055 | public function saveGuestList(string $filename = '', $multicall = false) 1056 | { 1057 | $filename = $this->secureUtf8($filename); 1058 | 1059 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 1060 | } 1061 | 1062 | /** 1063 | * Sets whether buddy notifications should be sent in the chat. 1064 | * Only available to Admin. 1065 | * @param mixed $player Login or player object; empty for global setting 1066 | * @param bool $enable 1067 | * @param bool $multicall 1068 | * @return bool 1069 | * @throws InvalidArgumentException 1070 | */ 1071 | public function setBuddyNotification($player, bool $enable, $multicall = false) 1072 | { 1073 | $login = $this->getLogin($player, true); 1074 | if ($login === false) { 1075 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1076 | } 1077 | 1078 | return $this->execute(ucfirst(__FUNCTION__), [$login, $enable], $multicall); 1079 | } 1080 | 1081 | /** 1082 | * Gets whether buddy notifications are enabled. 1083 | * @param mixed $player Login or player object; empty for global setting 1084 | * @param bool $multicall 1085 | * @return bool 1086 | * @throws InvalidArgumentException 1087 | */ 1088 | public function getBuddyNotification($player = null, $multicall = false) 1089 | { 1090 | $login = $this->getLogin($player, true); 1091 | if ($login === false) { 1092 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1093 | } 1094 | 1095 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 1096 | } 1097 | 1098 | /** 1099 | * Write the data to the specified file. 1100 | * Only available to Admin. 1101 | * @param string $filename Relative to the Maps path 1102 | * @param string $data 1103 | * @param bool $multicall 1104 | * @return bool 1105 | * @throws InvalidArgumentException 1106 | */ 1107 | public function writeFile(string $filename, string $data, $multicall = false) 1108 | { 1109 | $filename = $this->secureUtf8($filename); 1110 | 1111 | $data = new Xmlrpc\Base64($data); 1112 | return $this->execute(ucfirst(__FUNCTION__), [$filename, $data], $multicall); 1113 | } 1114 | 1115 | /** 1116 | * Send the data to the specified player. Login can be a single login or a list of comma-separated logins. 1117 | * Only available to Admin. 1118 | * @param mixed $recipient Login or player object or array 1119 | * @param string $filename 1120 | * @param bool $multicall 1121 | * @return bool 1122 | * @throws InvalidArgumentException 1123 | */ 1124 | public function tunnelSendDataFromFile($recipient, $filename, $multicall = false) 1125 | { 1126 | if (!file_exists($filename)) { 1127 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 1128 | } 1129 | 1130 | $contents = file_get_contents($filename); 1131 | return $this->tunnelSendData($recipient, $contents, $multicall); 1132 | } 1133 | 1134 | /** 1135 | * Send the data to the specified player. Login can be a single login or a list of comma-separated logins. 1136 | * Only available to Admin. 1137 | * @param mixed $recipient Login, player object or array 1138 | * @param string $data 1139 | * @param bool $multicall 1140 | * @return bool 1141 | * @throws InvalidArgumentException 1142 | */ 1143 | public function tunnelSendData($recipient, $data, $multicall = false) 1144 | { 1145 | $logins = $this->getLogins($recipient); 1146 | if ($logins === false) { 1147 | throw new InvalidArgumentException('recipient = ' . print_r($recipient, true)); 1148 | } 1149 | if (!is_string($data)) { 1150 | throw new InvalidArgumentException('data = ' . print_r($data, true)); 1151 | } 1152 | 1153 | $data = new Xmlrpc\Base64($data); 1154 | return $this->execute(ucfirst(__FUNCTION__) . 'ToLogin', [$logins, $data], $multicall); 1155 | } 1156 | 1157 | /** 1158 | * Just log the parameters and invoke a callback. 1159 | * Can be used to talk to other xmlrpc clients connected, or to make custom votes. 1160 | * If used in a callvote, the first parameter will be used as the vote message on the clients. 1161 | * Only available to Admin. 1162 | * @param string $message 1163 | * @param string $callback 1164 | * @param bool $multicall 1165 | * @return bool 1166 | * @throws InvalidArgumentException 1167 | */ 1168 | public function dedicatedEcho($message, $callback = '', $multicall = false) 1169 | { 1170 | if (!is_string($message)) { 1171 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 1172 | } 1173 | if (!is_string($callback)) { 1174 | throw new InvalidArgumentException('callback = ' . print_r($callback, true)); 1175 | } 1176 | 1177 | return $this->execute('Echo', [$message, $callback], $multicall); 1178 | } 1179 | 1180 | /** 1181 | * Ignore the player with the specified login. 1182 | * Only available to Admin. 1183 | * @param mixed $player Login or player object 1184 | * @param bool $multicall 1185 | * @return bool 1186 | * @throws InvalidArgumentException 1187 | */ 1188 | public function ignore($player, $multicall = false) 1189 | { 1190 | $login = $this->getLogin($player); 1191 | if ($login === false) { 1192 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1193 | } 1194 | 1195 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 1196 | } 1197 | 1198 | /** 1199 | * Unignore the player with the specified login. 1200 | * Only available to Admin. 1201 | * @param mixed $player Login or player object 1202 | * @param bool $multicall 1203 | * @return bool 1204 | * @throws InvalidArgumentException 1205 | */ 1206 | public function unIgnore($player, $multicall = false) 1207 | { 1208 | $login = $this->getLogin($player); 1209 | if ($login === false) { 1210 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1211 | } 1212 | 1213 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 1214 | } 1215 | 1216 | /** 1217 | * Clean the ignore list of the server. 1218 | * Only available to Admin. 1219 | * @param bool $multicall 1220 | * @return bool 1221 | */ 1222 | public function cleanIgnoreList($multicall = false) 1223 | { 1224 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1225 | } 1226 | 1227 | /** 1228 | * Returns the list of ignored players. 1229 | * @param int $length Maximum number of infos to be returned 1230 | * @param int $offset Starting index in the list 1231 | * @param bool $multicall 1232 | * @return Structures\Player[] 1233 | * @throws InvalidArgumentException 1234 | */ 1235 | public function getIgnoreList($length = -1, $offset = 0, $multicall = false) 1236 | { 1237 | if (!is_int($length)) { 1238 | throw new InvalidArgumentException('length = ' . print_r($length, true)); 1239 | } 1240 | if (!is_int($offset)) { 1241 | throw new InvalidArgumentException('offset = ' . print_r($offset, true)); 1242 | } 1243 | 1244 | if ($multicall) { 1245 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('Player', true)); 1246 | } 1247 | return Structures\Player::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 1248 | } 1249 | 1250 | /** 1251 | * Pay planets from the server account to a player. 1252 | * The creation of the transaction itself may cost planets, so you need to have planets on the server account. 1253 | * Only available to Admin. 1254 | * @param mixed $payee Login or player object 1255 | * @param int $amount 1256 | * @param string $message 1257 | * @param bool $multicall 1258 | * @return int BillId 1259 | * @throws InvalidArgumentException 1260 | */ 1261 | public function pay($payee, $amount, $message = '', $multicall = false) 1262 | { 1263 | $login = $this->getLogin($payee); 1264 | if ($login === false) { 1265 | throw new InvalidArgumentException('payee = ' . print_r($payee, true)); 1266 | } 1267 | if (!is_int($amount) || $amount < 1) { 1268 | throw new InvalidArgumentException('amount = ' . print_r($amount, true)); 1269 | } 1270 | if (!is_string($message)) { 1271 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 1272 | } 1273 | 1274 | return $this->execute(ucfirst(__FUNCTION__), [$login, $amount, $message], $multicall); 1275 | } 1276 | 1277 | /** 1278 | * Create a bill, send it to a player, and return the BillId. 1279 | * The creation of the transaction itself may cost planets, so you need to have planets on the server account. 1280 | * Only available to Admin. 1281 | * @param mixed $payer Login or player object 1282 | * @param int $amount 1283 | * @param string $message 1284 | * @param mixed $payee Login or player object; empty for server account 1285 | * @param bool $multicall 1286 | * @return int BillId 1287 | * @throws InvalidArgumentException 1288 | */ 1289 | public function sendBill($payer, $amount, $message = '', $payee = null, $multicall = false) 1290 | { 1291 | $payerLogin = $this->getLogin($payer); 1292 | if ($payerLogin === false) { 1293 | throw new InvalidArgumentException('payer = ' . print_r($payer, true)); 1294 | } 1295 | if (!is_int($amount) || $amount < 1) { 1296 | throw new InvalidArgumentException('amount = ' . print_r($amount, true)); 1297 | } 1298 | if (!is_string($message)) { 1299 | throw new InvalidArgumentException('message = ' . print_r($message, true)); 1300 | } 1301 | $payeeLogin = $this->getLogin($payee, true); 1302 | if ($payeeLogin === false) { 1303 | throw new InvalidArgumentException('payee = ' . print_r($payee, true)); 1304 | } 1305 | 1306 | return $this->execute(ucfirst(__FUNCTION__), [$payerLogin, $amount, $message, $payeeLogin], $multicall); 1307 | } 1308 | 1309 | /** 1310 | * Returns the current state of a bill. 1311 | * @param int $billId 1312 | * @param bool $multicall 1313 | * @return Structures\Bill 1314 | * @throws InvalidArgumentException 1315 | */ 1316 | public function getBillState($billId, $multicall = false) 1317 | { 1318 | if (!is_int($billId)) { 1319 | throw new InvalidArgumentException('billId = ' . print_r($billId, true)); 1320 | } 1321 | 1322 | if ($multicall) { 1323 | return $this->execute(ucfirst(__FUNCTION__), [$billId], $this->structHandler('Bill')); 1324 | } 1325 | return Structures\Bill::fromArray($this->execute(ucfirst(__FUNCTION__), [$billId])); 1326 | } 1327 | 1328 | /** 1329 | * Returns the current number of planets on the server account. 1330 | * @param bool $multicall 1331 | * @return int 1332 | */ 1333 | public function getServerPlanets($multicall = false) 1334 | { 1335 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1336 | } 1337 | 1338 | /** 1339 | * Get some system infos, including connection rates (in kbps). 1340 | * @param bool $multicall 1341 | * @return Structures\SystemInfos 1342 | */ 1343 | public function getSystemInfo($multicall = false) 1344 | { 1345 | if ($multicall) { 1346 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('SystemInfos')); 1347 | } 1348 | return Structures\SystemInfos::fromArray($this->execute(ucfirst(__FUNCTION__))); 1349 | } 1350 | 1351 | /** 1352 | * Set the download and upload rates (in kbps). 1353 | * @param int $downloadRate 1354 | * @param int $uploadRate 1355 | * @param bool $multicall 1356 | * @return bool 1357 | * @throws InvalidArgumentException 1358 | */ 1359 | public function setConnectionRates($downloadRate, $uploadRate, $multicall = false) 1360 | { 1361 | if (!is_int($downloadRate)) { 1362 | throw new InvalidArgumentException('downloadRate = ' . print_r($downloadRate, true)); 1363 | } 1364 | if (!is_int($uploadRate)) { 1365 | throw new InvalidArgumentException('uploadRate = ' . print_r($uploadRate, true)); 1366 | } 1367 | 1368 | return $this->execute(ucfirst(__FUNCTION__), [$downloadRate, $uploadRate], $multicall); 1369 | } 1370 | 1371 | /** 1372 | * Returns the list of tags and associated values set on this server. 1373 | * Only available to Admin. 1374 | * @param bool $multicall 1375 | * @return Structures\Tag[] 1376 | */ 1377 | public function getServerTags($multicall = false) 1378 | { 1379 | if ($multicall) { 1380 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Tag', true)); 1381 | } 1382 | return Structures\Tag::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__))); 1383 | } 1384 | 1385 | /** 1386 | * Set a tag and its value on the server. 1387 | * Only available to Admin. 1388 | * @param string $key 1389 | * @param string $value 1390 | * @param bool $multicall 1391 | * @return bool 1392 | */ 1393 | public function setServerTag(string $key, string $value, $multicall = false) 1394 | { 1395 | return $this->execute(ucfirst(__FUNCTION__), [$key, $value], $multicall); 1396 | } 1397 | 1398 | /** 1399 | * Unset the tag with the specified name on the server. 1400 | * Only available to Admin. 1401 | * @param string $key 1402 | * @param bool $multicall 1403 | * @return bool 1404 | */ 1405 | public function unsetServerTag(string $key, $multicall = false) 1406 | { 1407 | return $this->execute(ucfirst(__FUNCTION__), [$key], $multicall); 1408 | } 1409 | 1410 | /** 1411 | * Reset all tags on the server. 1412 | * Only available to Admin. 1413 | * @param bool $multicall 1414 | * @return bool 1415 | */ 1416 | public function resetServerTags($multicall = false) 1417 | { 1418 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1419 | } 1420 | 1421 | /** 1422 | * Set a new server name in utf8 format. 1423 | * Only available to Admin. 1424 | * @param string $name 1425 | * @param bool $multicall 1426 | * @return bool 1427 | */ 1428 | public function setServerName(string $name, $multicall = false) 1429 | { 1430 | return $this->execute(ucfirst(__FUNCTION__), [$name], $multicall); 1431 | } 1432 | 1433 | /** 1434 | * Get the server name in utf8 format. 1435 | * @param bool $multicall 1436 | * @return string 1437 | */ 1438 | public function getServerName($multicall = false) 1439 | { 1440 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1441 | } 1442 | 1443 | /** 1444 | * Set a new server comment in utf8 format. 1445 | * Only available to Admin. 1446 | * @param string $comment 1447 | * @param bool $multicall 1448 | * @return bool 1449 | */ 1450 | public function setServerComment(string $comment, $multicall = false) 1451 | { 1452 | return $this->execute(ucfirst(__FUNCTION__), [$comment], $multicall); 1453 | } 1454 | 1455 | /** 1456 | * Get the server comment in utf8 format. 1457 | * @param bool $multicall 1458 | * @return string 1459 | */ 1460 | public function getServerComment($multicall = false) 1461 | { 1462 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1463 | } 1464 | 1465 | /** 1466 | * Set whether the server should be hidden from the public server list. 1467 | * Only available to Admin. 1468 | * @param int $visibility 0: visible, 1: always hidden, 2: hidden from nations 1469 | * @param bool $multicall 1470 | * @return bool 1471 | * @throws InvalidArgumentException 1472 | */ 1473 | public function setHideServer($visibility, $multicall = false) 1474 | { 1475 | if (!is_int($visibility) || $visibility < 0 || $visibility > 2) { 1476 | throw new InvalidArgumentException('visibility = ' . print_r($visibility, true)); 1477 | } 1478 | 1479 | return $this->execute(ucfirst(__FUNCTION__), [$visibility], $multicall); 1480 | } 1481 | 1482 | /** 1483 | * Get whether the server wants to be hidden from the public server list. 1484 | * @param bool $multicall 1485 | * @return int 1486 | */ 1487 | public function getHideServer($multicall = false) 1488 | { 1489 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1490 | } 1491 | 1492 | /** 1493 | * Returns true if this is a relay server. 1494 | * @param bool $multicall 1495 | * @return bool 1496 | */ 1497 | public function isRelayServer($multicall = false) 1498 | { 1499 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1500 | } 1501 | 1502 | /** 1503 | * Set a new password for the server. 1504 | * Only available to Admin. 1505 | * @param string $password 1506 | * @param bool $multicall 1507 | * @return bool 1508 | */ 1509 | public function setServerPassword(string $password, $multicall = false) 1510 | { 1511 | return $this->execute(ucfirst(__FUNCTION__), [$password], $multicall); 1512 | } 1513 | 1514 | /** 1515 | * Get the server password if called as Admin or Super Admin, else returns if a password is needed or not. 1516 | * @param bool $multicall 1517 | * @return string|bool 1518 | */ 1519 | public function getServerPassword($multicall = false) 1520 | { 1521 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1522 | } 1523 | 1524 | /** 1525 | * Set a new password for the spectator mode. 1526 | * Only available to Admin. 1527 | * @param string $password 1528 | * @param bool $multicall 1529 | * @return bool 1530 | * @throws InvalidArgumentException 1531 | */ 1532 | public function setServerPasswordForSpectator(string $password, $multicall = false) 1533 | { 1534 | return $this->execute(ucfirst(__FUNCTION__), [$password], $multicall); 1535 | } 1536 | 1537 | /** 1538 | * Get the password for spectator mode if called as Admin or Super Admin, else returns if a password is needed or not. 1539 | * @param bool $multicall 1540 | * @return string|bool 1541 | */ 1542 | public function getServerPasswordForSpectator($multicall = false) 1543 | { 1544 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1545 | } 1546 | 1547 | /** 1548 | * Set a new maximum number of players. 1549 | * Only available to Admin. 1550 | * Requires a map restart to be taken into account. 1551 | * @param int $maxPlayers 1552 | * @param bool $multicall 1553 | * @return bool 1554 | * @throws InvalidArgumentException 1555 | */ 1556 | public function setMaxPlayers(int $maxPlayers, $multicall = false) 1557 | { 1558 | return $this->execute(ucfirst(__FUNCTION__), [$maxPlayers], $multicall); 1559 | } 1560 | 1561 | /** 1562 | * Get the current and next maximum number of players allowed on server. 1563 | * @param bool $multicall 1564 | * @return int[] {int CurrentValue, int NextValue} 1565 | */ 1566 | public function getMaxPlayers($multicall = false) 1567 | { 1568 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1569 | } 1570 | 1571 | /** 1572 | * Set a new maximum number of spectators. 1573 | * Only available to Admin. 1574 | * Requires a map restart to be taken into account. 1575 | * @param int $maxSpectators 1576 | * @param bool $multicall 1577 | * @return bool 1578 | * @throws InvalidArgumentException 1579 | */ 1580 | public function setMaxSpectators(int $maxSpectators, $multicall = false) 1581 | { 1582 | return $this->execute(ucfirst(__FUNCTION__), [$maxSpectators], $multicall); 1583 | } 1584 | 1585 | /** 1586 | * Get the current and next maximum number of Spectators allowed on server. 1587 | * @param bool $multicall 1588 | * @return int[] {int CurrentValue, int NextValue} 1589 | */ 1590 | public function getMaxSpectators($multicall = false) 1591 | { 1592 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1593 | } 1594 | 1595 | /** 1596 | * Declare if the server is a lobby, the number and maximum number of players currently managed by it, and the average level of the players. 1597 | * Only available to Admin. 1598 | * @param bool $isLobby 1599 | * @param int $currentPlayers 1600 | * @param int $maxPlayers 1601 | * @param float $averageLevel 1602 | * @param bool $multicall 1603 | * @return bool 1604 | * @throws InvalidArgumentException 1605 | */ 1606 | public function setLobbyInfo($isLobby, $currentPlayers, $maxPlayers, $averageLevel, $multicall = false) 1607 | { 1608 | if (!is_bool($isLobby)) { 1609 | throw new InvalidArgumentException('isLobby = ' . print_r($isLobby, true)); 1610 | } 1611 | if (!is_int($currentPlayers)) { 1612 | throw new InvalidArgumentException('currentPlayers = ' . print_r($currentPlayers, true)); 1613 | } 1614 | if (!is_int($maxPlayers)) { 1615 | throw new InvalidArgumentException('maxPlayers = ' . print_r($maxPlayers, true)); 1616 | } 1617 | if (!is_float($averageLevel)) { 1618 | throw new InvalidArgumentException('averageLevel = ' . print_r($averageLevel, true)); 1619 | } 1620 | 1621 | return $this->execute(ucfirst(__FUNCTION__), [$isLobby, $currentPlayers, $maxPlayers, $averageLevel], $multicall); 1622 | } 1623 | 1624 | /** 1625 | * Get whether the server if a lobby, the number and maximum number of players currently managed by it. 1626 | * @param bool $multicall 1627 | * @return Structures\LobbyInfo 1628 | */ 1629 | public function getLobbyInfo($multicall = false) 1630 | { 1631 | if ($multicall) { 1632 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('LobbyInfo')); 1633 | } 1634 | return Structures\LobbyInfo::fromArray($this->execute(ucfirst(__FUNCTION__))); 1635 | } 1636 | 1637 | /** 1638 | * Customize the clients 'leave server' dialog box. 1639 | * Only available to Admin. 1640 | * @param string $manialink 1641 | * @param string $sendToServer Server URL, eg. '#qjoin=login@title' 1642 | * @param bool $askFavorite 1643 | * @param int $quitButtonDelay In milliseconds 1644 | * @param bool $multicall 1645 | * @return bool 1646 | */ 1647 | public function customizeQuitDialog(string $manialink, string$sendToServer = '', bool $askFavorite = true, int $quitButtonDelay = 0, $multicall = false) 1648 | { 1649 | return $this->execute(ucfirst(__FUNCTION__), [$manialink, $sendToServer, $askFavorite, $quitButtonDelay], $multicall); 1650 | } 1651 | 1652 | /** 1653 | * Set whether, when a player is switching to spectator, the server should still consider him a player and keep his player slot, or not. 1654 | * Only available to Admin. 1655 | * @param bool $keep 1656 | * @param bool $multicall 1657 | * @return bool 1658 | * @throws InvalidArgumentException 1659 | */ 1660 | public function keepPlayerSlots(bool $keep = true, $multicall = false) 1661 | { 1662 | return $this->execute(ucfirst(__FUNCTION__), [$keep], $multicall); 1663 | } 1664 | 1665 | /** 1666 | * Get whether the server keeps player slots when switching to spectator. 1667 | * @param bool $multicall 1668 | * @return bool 1669 | */ 1670 | public function isKeepingPlayerSlots($multicall = false) 1671 | { 1672 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1673 | } 1674 | 1675 | /** 1676 | * Enable or disable peer-to-peer upload from server. 1677 | * Only available to Admin. 1678 | * @param bool $enable 1679 | * @param bool $multicall 1680 | * @return bool 1681 | */ 1682 | public function enableP2PUpload(bool $enable = true, $multicall = false) 1683 | { 1684 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 1685 | } 1686 | 1687 | /** 1688 | * Returns if the peer-to-peer upload from server is enabled. 1689 | * @param bool $multicall 1690 | * @return bool 1691 | */ 1692 | public function isP2PUpload($multicall = false) 1693 | { 1694 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1695 | } 1696 | 1697 | /** 1698 | * Enable or disable peer-to-peer download for server. 1699 | * Only available to Admin. 1700 | * @param bool $enable 1701 | * @param bool $multicall 1702 | * @return bool 1703 | * @throws InvalidArgumentException 1704 | */ 1705 | public function enableP2PDownload($enable = true, $multicall = false) 1706 | { 1707 | if (!is_bool($enable)) { 1708 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 1709 | } 1710 | 1711 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 1712 | } 1713 | 1714 | /** 1715 | * Returns if the peer-to-peer download for server is enabled. 1716 | * @param bool $multicall 1717 | * @return bool 1718 | */ 1719 | public function isP2PDownload($multicall = false) 1720 | { 1721 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1722 | } 1723 | 1724 | /** 1725 | * Allow clients to download maps from the server. 1726 | * Only available to Admin. 1727 | * @param bool $allow 1728 | * @param bool $multicall 1729 | * @return bool 1730 | * @throws InvalidArgumentException 1731 | */ 1732 | public function allowMapDownload($allow = true, $multicall = false) 1733 | { 1734 | if (!is_bool($allow)) { 1735 | throw new InvalidArgumentException('allow = ' . print_r($allow, true)); 1736 | } 1737 | 1738 | return $this->execute(ucfirst(__FUNCTION__), [$allow], $multicall); 1739 | } 1740 | 1741 | /** 1742 | * Returns if clients can download maps from the server. 1743 | * @param bool $multicall 1744 | * @return bool 1745 | */ 1746 | public function isMapDownloadAllowed($multicall = false) 1747 | { 1748 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1749 | } 1750 | 1751 | /** 1752 | * Returns the path of the game datas directory. 1753 | * Only available to Admin. 1754 | * @param bool $multicall 1755 | * @return string 1756 | */ 1757 | public function gameDataDirectory($multicall = false) 1758 | { 1759 | if ($multicall) { 1760 | return $this->execute(ucfirst(__FUNCTION__), [], [$this, 'stripBom']); 1761 | } 1762 | return $this->stripBom($this->execute(ucfirst(__FUNCTION__))); 1763 | } 1764 | 1765 | /** 1766 | * Returns the path of the maps directory. 1767 | * Only available to Admin. 1768 | * @param bool $multicall 1769 | * @return string 1770 | */ 1771 | public function getMapsDirectory($multicall = false) 1772 | { 1773 | if ($multicall) { 1774 | return $this->execute(ucfirst(__FUNCTION__), [], [$this, 'stripBom']); 1775 | } 1776 | return $this->stripBom($this->execute(ucfirst(__FUNCTION__))); 1777 | } 1778 | 1779 | /** 1780 | * Returns the path of the skins directory. 1781 | * Only available to Admin. 1782 | * @param bool $multicall 1783 | * @return string 1784 | */ 1785 | public function getSkinsDirectory($multicall = false) 1786 | { 1787 | if ($multicall) { 1788 | return $this->execute(ucfirst(__FUNCTION__), [], [$this, 'stripBom']); 1789 | } 1790 | return $this->stripBom($this->execute(ucfirst(__FUNCTION__))); 1791 | } 1792 | 1793 | /** 1794 | * Return info for a given team. 1795 | * Only available to Admin. 1796 | * @param int $team 0: no clan, 1 or 2 1797 | * @param bool $multicall 1798 | * @return Structures\Team 1799 | * @throws InvalidArgumentException 1800 | */ 1801 | public function getTeamInfo($team, $multicall = false) 1802 | { 1803 | if (!is_int($team) || $team < 0 || $team > 2) { 1804 | throw new InvalidArgumentException('team = ' . print_r($team, true)); 1805 | } 1806 | 1807 | if ($multicall) { 1808 | return $this->execute(ucfirst(__FUNCTION__), [$team], $this->structHandler('Team')); 1809 | } 1810 | return Structures\Team::fromArray($this->execute(ucfirst(__FUNCTION__), [$team])); 1811 | } 1812 | 1813 | /** 1814 | * Set the clublinks to use for the two teams. 1815 | * Only available to Admin. 1816 | * @param string $team1 1817 | * @param string $team2 1818 | * @param bool $multicall 1819 | * @return bool 1820 | * @throws InvalidArgumentException 1821 | */ 1822 | public function setForcedClubLinks(string $team1, string $team2, $multicall = false) 1823 | { 1824 | return $this->execute(ucfirst(__FUNCTION__), [$team1, $team2], $multicall); 1825 | } 1826 | 1827 | /** 1828 | * Get the forced clublinks. 1829 | * @param bool $multicall 1830 | * @return string[] 1831 | */ 1832 | public function getForcedClubLinks($multicall = false) 1833 | { 1834 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1835 | } 1836 | 1837 | /** 1838 | * (debug tool) Connect a fake player to the server and returns the login. 1839 | * Only available to Admin. 1840 | * @param bool $multicall 1841 | * @return string 1842 | */ 1843 | public function connectFakePlayer($multicall = false) 1844 | { 1845 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1846 | } 1847 | 1848 | /** 1849 | * (debug tool) Disconnect a fake player. 1850 | * Only available to Admin. 1851 | * @param string $login Fake player login or '*' for all 1852 | * @param bool $multicall 1853 | * @return bool 1854 | */ 1855 | public function disconnectFakePlayer(string $login, $multicall = false) 1856 | { 1857 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 1858 | } 1859 | 1860 | /** 1861 | * Returns the token infos for a player. 1862 | * @param mixed $player Login or player object 1863 | * @param bool $multicall 1864 | * @return Structures\TokenInfos 1865 | * @throws InvalidArgumentException 1866 | */ 1867 | public function getDemoTokenInfosForPlayer($player, $multicall = false) 1868 | { 1869 | $login = $this->getLogin($player); 1870 | if ($login === false) { 1871 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 1872 | } 1873 | 1874 | if ($multicall) { 1875 | return $this->execute(ucfirst(__FUNCTION__), [$login], $this->structHandler('TokenInfos')); 1876 | } 1877 | return Structures\TokenInfos::fromArray($this->execute(ucfirst(__FUNCTION__), [$login])); 1878 | } 1879 | 1880 | /** 1881 | * Disable player horns. 1882 | * Only available to Admin. 1883 | * @param bool $disable 1884 | * @param bool $multicall 1885 | * @return bool 1886 | */ 1887 | public function disableHorns(bool $disable = true, $multicall = false) 1888 | { 1889 | return $this->execute(ucfirst(__FUNCTION__), [$disable], $multicall); 1890 | } 1891 | 1892 | /** 1893 | * Returns whether the horns are disabled. 1894 | * @param bool $multicall 1895 | * @return bool 1896 | */ 1897 | public function areHornsDisabled($multicall = false): bool 1898 | { 1899 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1900 | } 1901 | 1902 | /** 1903 | * Disable the automatic mesages when a player connects/disconnects from the server. 1904 | * Only available to Admin. 1905 | * @param bool $disable 1906 | * @param bool $multicall 1907 | * @return bool 1908 | */ 1909 | public function disableServiceAnnounces(bool $disable = true, $multicall = false) 1910 | { 1911 | return $this->execute(ucfirst(__FUNCTION__), [$disable], $multicall); 1912 | } 1913 | 1914 | /** 1915 | * Returns whether the automatic mesages are disabled. 1916 | * @param bool $multicall 1917 | * @return bool 1918 | */ 1919 | public function areServiceAnnouncesDisabled($multicall = false) 1920 | { 1921 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1922 | } 1923 | 1924 | /** 1925 | * Enable the autosaving of all replays (vizualisable replays with all players, but not validable) on the server. 1926 | * Only available to SuperAdmin. 1927 | * @param bool $enable 1928 | * @param bool $multicall 1929 | * @return bool 1930 | * @throws InvalidArgumentException 1931 | */ 1932 | public function autoSaveReplays(bool $enable = true, $multicall = false) 1933 | { 1934 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 1935 | } 1936 | 1937 | /** 1938 | * Enable the autosaving on the server of validation replays, every time a player makes a new time. 1939 | * Only available to SuperAdmin. 1940 | * @param bool $enable 1941 | * @param bool $multicall 1942 | * @return bool 1943 | * @throws InvalidArgumentException 1944 | */ 1945 | public function autoSaveValidationReplays($enable = true, $multicall = false) 1946 | { 1947 | if (!is_bool($enable)) { 1948 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 1949 | } 1950 | 1951 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 1952 | } 1953 | 1954 | /** 1955 | * Returns if autosaving of all replays is enabled on the server. 1956 | * @param bool $multicall 1957 | * @return bool 1958 | */ 1959 | public function isAutoSaveReplaysEnabled($multicall = false) 1960 | { 1961 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1962 | } 1963 | 1964 | /** 1965 | * Returns if autosaving of validation replays is enabled on the server. 1966 | * @param bool $multicall 1967 | * @return bool 1968 | */ 1969 | public function isAutoSaveValidationReplaysEnabled($multicall = false) 1970 | { 1971 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 1972 | } 1973 | 1974 | /** 1975 | * Saves the current replay (vizualisable replays with all players, but not validable). 1976 | * Only available to Admin. 1977 | * @param string $filename Empty for automatic filename 1978 | * @param bool $multicall 1979 | * @return bool 1980 | * @throws InvalidArgumentException 1981 | */ 1982 | public function saveCurrentReplay(string $filename = '', $multicall = false) 1983 | { 1984 | $filename = $this->secureUtf8($filename); 1985 | 1986 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 1987 | } 1988 | 1989 | /** 1990 | * Saves a replay with the ghost of all the players' best race. 1991 | * Only available to Admin. 1992 | * @param mixed $player Login or player object; empty for all 1993 | * @param string $filename Empty for automatic filename 1994 | * @param bool $multicall 1995 | * @return bool 1996 | * @throws InvalidArgumentException 1997 | */ 1998 | public function saveBestGhostsReplay($player = null, string $filename = '', $multicall = false) 1999 | { 2000 | $login = $this->getLogin($player, true); 2001 | if ($login === false) { 2002 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 2003 | } 2004 | $filename = $this->secureUtf8($filename); 2005 | 2006 | return $this->execute(ucfirst(__FUNCTION__), [$login, $filename], $multicall); 2007 | } 2008 | 2009 | /** 2010 | * Returns a replay containing the data needed to validate the current best time of the player. 2011 | * @param mixed $player Login or player object 2012 | * @param bool $multicall 2013 | * @return string 2014 | * @throws InvalidArgumentException 2015 | */ 2016 | public function getValidationReplay($player, $multicall = false) 2017 | { 2018 | $login = $this->getLogin($player); 2019 | if ($login === false) { 2020 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 2021 | } 2022 | 2023 | if ($multicall) { 2024 | return $this->execute(ucfirst(__FUNCTION__), [$login], function ($v) { 2025 | return $v->scalar; 2026 | }); 2027 | } 2028 | return $this->execute(ucfirst(__FUNCTION__), [$login])->scalar; 2029 | } 2030 | 2031 | /** 2032 | * Set a new ladder mode. 2033 | * Only available to Admin. 2034 | * Requires a map restart to be taken into account. 2035 | * @param int $mode 0: disabled, 1: forced 2036 | * @param bool $multicall 2037 | * @return bool 2038 | * @throws InvalidArgumentException 2039 | */ 2040 | public function setLadderMode($mode, $multicall = false) 2041 | { 2042 | if ($mode !== 0 && $mode !== 1) { 2043 | throw new InvalidArgumentException('mode = ' . print_r($mode, true)); 2044 | } 2045 | 2046 | return $this->execute(ucfirst(__FUNCTION__), [$mode], $multicall); 2047 | } 2048 | 2049 | /** 2050 | * Get the current and next ladder mode on server. 2051 | * @param bool $multicall 2052 | * @return int[] {int CurrentValue, int NextValue} 2053 | */ 2054 | public function getLadderMode($multicall = false) 2055 | { 2056 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2057 | } 2058 | 2059 | /** 2060 | * Get the ladder points limit for the players allowed on this server. 2061 | * @param bool $multicall 2062 | * @return Structures\LadderLimits 2063 | */ 2064 | public function getLadderServerLimits($multicall = false) 2065 | { 2066 | if ($multicall) { 2067 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('LadderLimits')); 2068 | } 2069 | return Structures\LadderLimits::fromArray($this->execute(ucfirst(__FUNCTION__))); 2070 | } 2071 | 2072 | /** 2073 | * Set the network vehicle quality. 2074 | * Only available to Admin. 2075 | * Requires a map restart to be taken into account. 2076 | * @param int $quality 0: fast, 1: high 2077 | * @param bool $multicall 2078 | * @return bool 2079 | * @throws InvalidArgumentException 2080 | */ 2081 | public function setVehicleNetQuality($quality, $multicall = false) 2082 | { 2083 | if ($quality !== 0 && $quality !== 1) { 2084 | throw new InvalidArgumentException('quality = ' . print_r($quality, true)); 2085 | } 2086 | 2087 | return $this->execute(ucfirst(__FUNCTION__), [$quality], $multicall); 2088 | } 2089 | 2090 | /** 2091 | * Get the current and next network vehicle quality on server. 2092 | * @param bool $multicall 2093 | * @return int[] {int CurrentValue, int NextValue} 2094 | */ 2095 | public function getVehicleNetQuality($multicall = false) 2096 | { 2097 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2098 | } 2099 | 2100 | /** 2101 | * Set new server options using the struct passed as parameters. 2102 | * Mandatory fields: 2103 | * Name, Comment, Password, PasswordForSpectator, NextCallVoteTimeOut and CallVoteRatio. 2104 | * Ignored fields: 2105 | * LadderServerLimitMin, LadderServerLimitMax and those starting with Current. 2106 | * All other fields are optional and can be set to null to be ignored. 2107 | * Only available to Admin. 2108 | * A change of any field starting with Next requires a map restart to be taken into account. 2109 | * @param Structures\ServerOptions $options 2110 | * @param bool $multicall 2111 | * @return bool 2112 | * @throws InvalidArgumentException 2113 | */ 2114 | public function setServerOptions(ServerOptions $options, $multicall = false) 2115 | { 2116 | if ($options->isValid() === false) { 2117 | throw new InvalidArgumentException('options = ' . print_r($options, true)); 2118 | } 2119 | 2120 | return $this->execute(ucfirst(__FUNCTION__), [$options->toSetterArray()], $multicall); 2121 | } 2122 | 2123 | /** 2124 | * Returns a struct containing the server options 2125 | * @param bool $multicall 2126 | * @return Structures\ServerOptions 2127 | */ 2128 | public function getServerOptions($multicall = false) 2129 | { 2130 | if ($multicall) { 2131 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('ServerOptions')); 2132 | } 2133 | return Structures\ServerOptions::fromArray($this->execute(ucfirst(__FUNCTION__))); 2134 | } 2135 | 2136 | /** 2137 | * Set whether the players can choose their side or if the teams are forced by the server (using ForcePlayerTeam()). 2138 | * Only available to Admin. 2139 | * @param bool $enable 2140 | * @param bool $multicall 2141 | * @return bool 2142 | * @throws InvalidArgumentException 2143 | */ 2144 | public function setForcedTeams($enable, $multicall = false) 2145 | { 2146 | if (!is_bool($enable)) { 2147 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 2148 | } 2149 | 2150 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 2151 | } 2152 | 2153 | /** 2154 | * Returns whether the players can choose their side or if the teams are forced by the server. 2155 | * @param bool $multicall 2156 | * @return bool 2157 | */ 2158 | public function getForcedTeams($multicall = false) 2159 | { 2160 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2161 | } 2162 | 2163 | /** 2164 | * Defines the packmask of the server. 2165 | * Only maps matching the packmask will be allowed on the server, so that player connecting to it know what to expect. 2166 | * Only available when the server is stopped. 2167 | * Only available in 2011-08-01 API version. 2168 | * Only available to Admin. 2169 | * @param string $packMask 2170 | * @param bool $multicall 2171 | * @return bool 2172 | * @throws InvalidArgumentException 2173 | */ 2174 | public function setServerPackMask($packMask, $multicall = false) 2175 | { 2176 | if (!is_string($packMask)) { 2177 | throw new InvalidArgumentException('packMask = ' . print_r($packMask, true)); 2178 | } 2179 | 2180 | return $this->execute(ucfirst(__FUNCTION__), [$packMask], $multicall); 2181 | } 2182 | 2183 | /** 2184 | * Get the packmask of the server. 2185 | * Only available in 2011-08-01 API version. 2186 | * @param bool $multicall 2187 | * @return string 2188 | */ 2189 | public function getServerPackMask($multicall = false) 2190 | { 2191 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2192 | } 2193 | 2194 | /** 2195 | * Set the mods to apply on the clients. 2196 | * Only available to Admin. 2197 | * Requires a map restart to be taken into account. 2198 | * @param bool $override If true, even the maps with a mod will be overridden by the server setting 2199 | * @param Structures\Mod|Structures\Mod[] $mods Array of structures [{string Env, string Url}, ...] 2200 | * @param bool $multicall 2201 | * @return bool 2202 | * @throws InvalidArgumentException 2203 | */ 2204 | public function setForcedMods($override, $mods, $multicall = false) 2205 | { 2206 | if (!is_bool($override)) { 2207 | throw new InvalidArgumentException('override = ' . print_r($override, true)); 2208 | } 2209 | if (is_array($mods)) { 2210 | foreach ($mods as $i => &$mod) { 2211 | if (!($mod instanceof Structures\Mod)) { 2212 | throw new InvalidArgumentException('mods[' . $i . '] = ' . print_r($mod, true)); 2213 | } 2214 | $mod = $mod->toArray(); 2215 | } 2216 | } elseif ($mods instanceof Structures\Mod) { 2217 | $mods = [$mods->toArray()]; 2218 | } else { 2219 | throw new InvalidArgumentException('mods = ' . print_r($mods, true)); 2220 | } 2221 | 2222 | return $this->execute(ucfirst(__FUNCTION__), [$override, $mods], $multicall); 2223 | } 2224 | 2225 | /** 2226 | * Get the mods settings. 2227 | * @param bool $multicall 2228 | * @return array {bool Override, Structures\Mod[] Mods} 2229 | */ 2230 | public function getForcedMods($multicall = false) 2231 | { 2232 | if ($multicall) { 2233 | return $this->execute(ucfirst(__FUNCTION__), [], function ($v) { 2234 | $v['Mods'] = Structures\Mod::fromArrayOfArray($v['Mods']); 2235 | return $v; 2236 | }); 2237 | } 2238 | $result = $this->execute(ucfirst(__FUNCTION__)); 2239 | $result['Mods'] = Structures\Mod::fromArrayOfArray($result['Mods']); 2240 | return $result; 2241 | } 2242 | 2243 | /** 2244 | * Set the music to play on the clients. 2245 | * Only available to Admin. 2246 | * Requires a map restart to be taken into account. 2247 | * @param bool $override If true, even the maps with a custom music will be overridden by the server setting 2248 | * @param string $music Url or filename relative to the GameData path 2249 | * @param bool $multicall 2250 | * @return bool 2251 | * @throws InvalidArgumentException 2252 | */ 2253 | public function setForcedMusic(bool $override, string $music, $multicall = false) 2254 | { 2255 | if (!preg_match('~^.+?://~', $music)) { 2256 | $music = $this->secureUtf8($music); 2257 | } 2258 | 2259 | return $this->execute(ucfirst(__FUNCTION__), [$override, $music], $multicall); 2260 | } 2261 | 2262 | /** 2263 | * Get the music setting. 2264 | * @param bool $multicall 2265 | * @return Structures\Music 2266 | */ 2267 | public function getForcedMusic($multicall = false) 2268 | { 2269 | if ($multicall) { 2270 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Music')); 2271 | } 2272 | return Structures\Music::fromArray($this->execute(ucfirst(__FUNCTION__))); 2273 | } 2274 | 2275 | /** 2276 | * Defines a list of remappings for player skins. 2277 | * Will only affect players connecting after the value is set. 2278 | * Only available to Admin. 2279 | * @param Structures\ForcedSkin|Structures\ForcedSkin[] $skins List of structs {Orig, Name, Checksum, Url}: 2280 | * - Orig is the name of the skin to remap, or '*' for any other 2281 | * - Name, Checksum, Url define the skin to use (you may set value '' for any of those, all 3 null means same as Orig). 2282 | * @param bool $multicall 2283 | * @return bool 2284 | * @throws InvalidArgumentException 2285 | */ 2286 | public function setForcedSkins($skins, $multicall = false) 2287 | { 2288 | if (is_array($skins)) { 2289 | foreach ($skins as $i => &$skin) { 2290 | if (!($skin instanceof Structures\ForcedSkin)) { 2291 | throw new InvalidArgumentException('skins[' . $i . '] = ' . print_r($skin, true)); 2292 | } 2293 | $skin = $skin->toArray(); 2294 | } 2295 | } elseif ($skins instanceof Structures\ForcedSkin) { 2296 | $skins = [$skins->toArray()]; 2297 | } else { 2298 | throw new InvalidArgumentException('skins = ' . print_r($skins, true)); 2299 | } 2300 | 2301 | return $this->execute(ucfirst(__FUNCTION__), [$skins], $multicall); 2302 | } 2303 | 2304 | /** 2305 | * Get the current forced skins. 2306 | * @param bool $multicall 2307 | * @return Structures\ForcedSkin[] 2308 | */ 2309 | public function getForcedSkins($multicall = false) 2310 | { 2311 | if ($multicall) { 2312 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('ForcedSkin', true)); 2313 | } 2314 | return Structures\ForcedSkin::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__))); 2315 | } 2316 | 2317 | /** 2318 | * Returns the last error message for an internet connection. 2319 | * Only available to Admin. 2320 | * @param bool $multicall 2321 | * @return string 2322 | */ 2323 | public function getLastConnectionErrorMessage($multicall = false) 2324 | { 2325 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2326 | } 2327 | 2328 | /** 2329 | * Set a new password for the referee mode. 2330 | * Only available to Admin. 2331 | * @param string $password 2332 | * @param bool $multicall 2333 | * @return bool 2334 | * @throws InvalidArgumentException 2335 | */ 2336 | public function setRefereePassword(string $password, $multicall = false) 2337 | { 2338 | return $this->execute(ucfirst(__FUNCTION__), [$password], $multicall); 2339 | } 2340 | 2341 | /** 2342 | * Get the password for referee mode if called as Admin or Super Admin, else returns if a password is needed or not. 2343 | * @param bool $multicall 2344 | * @return string|bool 2345 | */ 2346 | public function getRefereePassword($multicall = false) 2347 | { 2348 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2349 | } 2350 | 2351 | /** 2352 | * Set the referee validation mode. 2353 | * Only available to Admin. 2354 | * @param int $mode 0: validate the top3 players, 1: validate all players 2355 | * @param bool $multicall 2356 | * @return bool 2357 | * @throws InvalidArgumentException 2358 | */ 2359 | public function setRefereeMode($mode, $multicall = false) 2360 | { 2361 | if ($mode !== 0 && $mode !== 1) { 2362 | throw new InvalidArgumentException('mode = ' . print_r($mode, true)); 2363 | } 2364 | 2365 | return $this->execute(ucfirst(__FUNCTION__), [$mode], $multicall); 2366 | } 2367 | 2368 | /** 2369 | * Get the referee validation mode. 2370 | * @param bool $multicall 2371 | * @return int 2372 | */ 2373 | public function getRefereeMode($multicall = false) 2374 | { 2375 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2376 | } 2377 | 2378 | /** 2379 | * Set whether the game should use a variable validation seed or not. 2380 | * Only available to Admin. 2381 | * Requires a map restart to be taken into account. 2382 | * @param bool $enable 2383 | * @param bool $multicall 2384 | * @return bool 2385 | * @throws InvalidArgumentException 2386 | */ 2387 | public function setUseChangingValidationSeed($enable, $multicall = false) 2388 | { 2389 | if (!is_bool($enable)) { 2390 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 2391 | } 2392 | 2393 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 2394 | } 2395 | 2396 | /** 2397 | * Get the current and next value of UseChangingValidationSeed. 2398 | * @param bool $multicall 2399 | * @return bool[] {bool CurrentValue, bool NextValue} 2400 | */ 2401 | public function getUseChangingValidationSeed($multicall = false) 2402 | { 2403 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2404 | } 2405 | 2406 | /** 2407 | * Set the maximum time the server must wait for inputs from the clients before dropping data, or '0' for auto-adaptation. 2408 | * Only used by ShootMania. 2409 | * Only available to Admin. 2410 | * @param int $latency 2411 | * @param bool $multicall 2412 | * @return bool 2413 | */ 2414 | public function setClientInputsMaxLatency(int $latency, $multicall = false) 2415 | { 2416 | return $this->execute(ucfirst(__FUNCTION__), [$latency], $multicall); 2417 | } 2418 | 2419 | /** 2420 | * Get the current ClientInputsMaxLatency. 2421 | * Only used by ShootMania. 2422 | * @param bool $multicall 2423 | * @return int 2424 | */ 2425 | public function getClientInputsMaxLatency($multicall = false) 2426 | { 2427 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2428 | } 2429 | 2430 | /** 2431 | * Sets whether the server is in warm-up phase or not. 2432 | * Only available to Admin. 2433 | * @param bool $enable 2434 | * @param bool $multicall 2435 | * @return bool 2436 | * @throws InvalidArgumentException 2437 | */ 2438 | public function setWarmUp($enable, $multicall = false) 2439 | { 2440 | if (!is_bool($enable)) { 2441 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 2442 | } 2443 | 2444 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 2445 | } 2446 | 2447 | /** 2448 | * Returns whether the server is in warm-up phase. 2449 | * @param bool $multicall 2450 | * @return bool 2451 | */ 2452 | public function getWarmUp($multicall = false) 2453 | { 2454 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2455 | } 2456 | 2457 | /** 2458 | * Get the current mode script. 2459 | * @param bool $multicall 2460 | * @return string 2461 | */ 2462 | public function getModeScriptText($multicall = false) 2463 | { 2464 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2465 | } 2466 | 2467 | /** 2468 | * Set the mode script and restart. 2469 | * Only available to Admin. 2470 | * @param string $script 2471 | * @param bool $multicall 2472 | * @return bool 2473 | * @throws InvalidArgumentException 2474 | */ 2475 | public function setModeScriptText($script, $multicall = false) 2476 | { 2477 | if (!is_string($script)) { 2478 | throw new InvalidArgumentException('script = ' . print_r($script, true)); 2479 | } 2480 | 2481 | return $this->execute(ucfirst(__FUNCTION__), [$script], $multicall); 2482 | } 2483 | 2484 | /** 2485 | * Returns the description of the current mode script. 2486 | * @param bool $multicall 2487 | * @return Structures\ScriptInfo 2488 | */ 2489 | public function getModeScriptInfo($multicall = false) 2490 | { 2491 | if ($multicall) { 2492 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('ScriptInfo')); 2493 | } 2494 | return Structures\ScriptInfo::fromArray($this->execute(ucfirst(__FUNCTION__))); 2495 | } 2496 | 2497 | /** 2498 | * Returns the current settings of the mode script. 2499 | * @param bool $multicall 2500 | * @return array {mixed , ...} 2501 | */ 2502 | public function getModeScriptSettings($multicall = false) 2503 | { 2504 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2505 | } 2506 | 2507 | /** 2508 | * Change the settings of the mode script. 2509 | * Only available to Admin. 2510 | * @param mixed[] $settings {mixed , ...} 2511 | * @param bool $multicall 2512 | * @return bool 2513 | * @throws InvalidArgumentException 2514 | */ 2515 | public function setModeScriptSettings($settings, $multicall = false) 2516 | { 2517 | if (!is_array($settings) || !$settings) { 2518 | throw new InvalidArgumentException('settings = ' . print_r($settings, true)); 2519 | } 2520 | 2521 | return $this->execute(ucfirst(__FUNCTION__), [$settings], $multicall); 2522 | } 2523 | 2524 | /** 2525 | * Send commands to the mode script. 2526 | * Only available to Admin. 2527 | * @param mixed[] $commands {mixed , ...} 2528 | * @param bool $multicall 2529 | * @return bool 2530 | * @throws InvalidArgumentException 2531 | */ 2532 | public function sendModeScriptCommands($commands, $multicall = false) 2533 | { 2534 | if (!is_array($commands) || !$commands) { 2535 | throw new InvalidArgumentException('commands = ' . print_r($commands, true)); 2536 | } 2537 | 2538 | return $this->execute(ucfirst(__FUNCTION__), [$commands], $multicall); 2539 | } 2540 | 2541 | /** 2542 | * Change the settings and send commands to the mode script. 2543 | * Only available to Admin. 2544 | * @param mixed[] $settings {mixed , ...} 2545 | * @param mixed[] $commands {mixed , ...} 2546 | * @param bool $multicall 2547 | * @return bool 2548 | * @throws InvalidArgumentException 2549 | */ 2550 | public function setModeScriptSettingsAndCommands($settings, $commands, $multicall = false) 2551 | { 2552 | if (!is_array($settings) || !$settings) { 2553 | throw new InvalidArgumentException('settings = ' . print_r($settings, true)); 2554 | } 2555 | if (!is_array($commands) || !$commands) { 2556 | throw new InvalidArgumentException('commands = ' . print_r($commands, true)); 2557 | } 2558 | 2559 | return $this->execute(ucfirst(__FUNCTION__), [$settings, $commands], $multicall); 2560 | } 2561 | 2562 | /** 2563 | * Returns the current xml-rpc variables of the mode script. 2564 | * @param bool $multicall 2565 | * @return array {mixed , ...} 2566 | */ 2567 | public function getModeScriptVariables($multicall = false) 2568 | { 2569 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2570 | } 2571 | 2572 | /** 2573 | * Set the xml-rpc variables of the mode script. 2574 | * Only available to Admin. 2575 | * @param mixed[] $variables {mixed , ...} 2576 | * @param bool $multicall 2577 | * @return bool 2578 | * @throws InvalidArgumentException 2579 | */ 2580 | public function setModeScriptVariables($variables, $multicall = false) 2581 | { 2582 | if (!is_array($variables) || !$variables) { 2583 | throw new InvalidArgumentException('variables = ' . print_r($variables, true)); 2584 | } 2585 | 2586 | return $this->execute(ucfirst(__FUNCTION__), [$variables], $multicall); 2587 | } 2588 | 2589 | /** 2590 | * Send an event to the mode script. 2591 | * Only available to Admin. 2592 | * @param string $event 2593 | * @param string|string[] $params 2594 | * @param bool $multicall 2595 | * @return bool 2596 | * @throws InvalidArgumentException 2597 | */ 2598 | public function triggerModeScriptEvent(string $event, $params = '', $multicall = false) 2599 | { 2600 | if (is_string($params)) { 2601 | return $this->execute(ucfirst(__FUNCTION__), [$event, $params], $multicall); 2602 | } 2603 | 2604 | if (is_array($params)) { 2605 | foreach ($params as $param) { 2606 | if (!is_string($param)) { 2607 | throw new InvalidArgumentException('argument must be a string: param = ' . print_r($param, true)); 2608 | } 2609 | } 2610 | return $this->execute(ucfirst(__FUNCTION__) . 'Array', [$event, $params], $multicall); 2611 | } 2612 | 2613 | // else 2614 | throw new InvalidArgumentException('argument must be string or string[]: params = ' . print_r($params, true)); 2615 | } 2616 | 2617 | /** 2618 | * Get the script cloud variables of given object. 2619 | * Only available to Admin. 2620 | * @param string $type 2621 | * @param string $id 2622 | * @param bool $multicall 2623 | * @return array {mixed , ...} 2624 | * @throws InvalidArgumentException 2625 | */ 2626 | public function getScriptCloudVariables($type, $id, $multicall = false) 2627 | { 2628 | if (!is_string($type)) { 2629 | throw new InvalidArgumentException('type = ' . print_r($type, true)); 2630 | } 2631 | if (!is_string($id)) { 2632 | throw new InvalidArgumentException('id = ' . print_r($id, true)); 2633 | } 2634 | 2635 | return $this->execute(ucfirst(__FUNCTION__), [$type, $id], $multicall); 2636 | } 2637 | 2638 | /** 2639 | * Set the script cloud variables of given object. 2640 | * Only available to Admin. 2641 | * @param string $type 2642 | * @param string $id 2643 | * @param mixed[] $variables {mixed , ...} 2644 | * @param bool $multicall 2645 | * @return bool 2646 | * @throws InvalidArgumentException 2647 | */ 2648 | public function setScriptCloudVariables($type, $id, $variables, $multicall = false) 2649 | { 2650 | if (!is_string($type)) { 2651 | throw new InvalidArgumentException('type = ' . print_r($type, true)); 2652 | } 2653 | if (!is_string($id)) { 2654 | throw new InvalidArgumentException('id = ' . print_r($id, true)); 2655 | } 2656 | if (!is_array($variables) || !$variables) { 2657 | throw new InvalidArgumentException('variables = ' . print_r($variables, true)); 2658 | } 2659 | 2660 | return $this->execute(ucfirst(__FUNCTION__), [$type, $id, $variables], $multicall); 2661 | } 2662 | 2663 | /** 2664 | * Restarts the map. 2665 | * Only available to Admin. 2666 | * @param bool $dontClearCupScores Only available in legacy cup mode 2667 | * @param bool $multicall 2668 | * @return bool 2669 | */ 2670 | public function restartMap($dontClearCupScores = false, $multicall = false) 2671 | { 2672 | if (!is_bool($dontClearCupScores)) { 2673 | throw new InvalidArgumentException('dontClearCupScores = ' . print_r($dontClearCupScores, true)); 2674 | } 2675 | 2676 | return $this->execute(ucfirst(__FUNCTION__), [$dontClearCupScores], $multicall); 2677 | } 2678 | 2679 | /** 2680 | * Switch to next map. 2681 | * Only available to Admin. 2682 | * @param bool $dontClearCupScores Only available in legacy cup mode 2683 | * @param bool $multicall 2684 | * @return bool 2685 | */ 2686 | public function nextMap($dontClearCupScores = false, $multicall = false) 2687 | { 2688 | if (!is_bool($dontClearCupScores)) { 2689 | throw new InvalidArgumentException('dontClearCupScores = ' . print_r($dontClearCupScores, true)); 2690 | } 2691 | 2692 | return $this->execute(ucfirst(__FUNCTION__), [$dontClearCupScores], $multicall); 2693 | } 2694 | 2695 | /** 2696 | * Attempt to balance teams. 2697 | * Only available to Admin. 2698 | * @param bool $multicall 2699 | * @return bool 2700 | */ 2701 | public function autoTeamBalance($multicall = false) 2702 | { 2703 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2704 | } 2705 | 2706 | /** 2707 | * Stop the server. 2708 | * Only available to SuperAdmin. 2709 | * @param bool $multicall 2710 | * @return bool 2711 | */ 2712 | public function stopServer($multicall = false) 2713 | { 2714 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2715 | } 2716 | 2717 | /** 2718 | * In legacy Rounds or Laps mode, force the end of round without waiting for all players to giveup/finish. 2719 | * Only available to Admin. 2720 | * @param bool $multicall 2721 | * @return bool 2722 | */ 2723 | public function forceEndRound($multicall = false) 2724 | { 2725 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2726 | } 2727 | 2728 | /** 2729 | * Set new game settings using the struct passed as parameters. 2730 | * Only available to Admin. 2731 | * Requires a map restart to be taken into account. 2732 | * @param Structures\GameInfos $gameInfos 2733 | * @param bool $multicall 2734 | * @return bool 2735 | * @throws InvalidArgumentException 2736 | */ 2737 | public function setGameInfos($gameInfos, $multicall = false) 2738 | { 2739 | if (!($gameInfos instanceof Structures\GameInfos)) { 2740 | throw new InvalidArgumentException('gameInfos = ' . print_r($gameInfos, true)); 2741 | } 2742 | 2743 | return $this->execute(ucfirst(__FUNCTION__), [$gameInfos->toArray()], $multicall); 2744 | } 2745 | 2746 | /** 2747 | * Returns a struct containing the current game settings. 2748 | * @param bool $multicall 2749 | * @return Structures\GameInfos 2750 | */ 2751 | public function getCurrentGameInfo($multicall = false) 2752 | { 2753 | if ($multicall) { 2754 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('GameInfos')); 2755 | } 2756 | return Structures\GameInfos::fromArray($this->execute(ucfirst(__FUNCTION__))); 2757 | } 2758 | 2759 | /** 2760 | * Returns a struct containing the game settings for the next map. 2761 | * @param bool $multicall 2762 | * @return Structures\GameInfos 2763 | */ 2764 | public function getNextGameInfo($multicall = false) 2765 | { 2766 | if ($multicall) { 2767 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('GameInfos')); 2768 | } 2769 | return Structures\GameInfos::fromArray($this->execute(ucfirst(__FUNCTION__))); 2770 | } 2771 | 2772 | /** 2773 | * Returns a struct containing two other structures, the first containing the current game settings and the second the game settings for next map. 2774 | * @param bool $multicall 2775 | * @return Structures\GameInfos[] {Structures\GameInfos CurrentGameInfos, Structures\GameInfos NextGameInfos} 2776 | */ 2777 | public function getGameInfos($multicall = false) 2778 | { 2779 | if ($multicall) { 2780 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('GameInfos', true)); 2781 | } 2782 | return Structures\GameInfos::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__))); 2783 | } 2784 | 2785 | /** 2786 | * Set a new game mode. 2787 | * Only available to Admin. 2788 | * Requires a map restart to be taken into account. 2789 | * @param int $gameMode 0: Script, 1: Rounds, 2: TimeAttack, 3: Team, 4: Laps, 5: Cup, 6: Stunt 2790 | * @param bool $multicall 2791 | * @return bool 2792 | * @throws InvalidArgumentException 2793 | */ 2794 | public function setGameMode($gameMode, $multicall = false) 2795 | { 2796 | if (!is_int($gameMode) || $gameMode < 0 || $gameMode > 6) { 2797 | throw new InvalidArgumentException('gameMode = ' . print_r($gameMode, true)); 2798 | } 2799 | 2800 | return $this->execute(ucfirst(__FUNCTION__), [$gameMode], $multicall); 2801 | } 2802 | 2803 | /** 2804 | * Get the current game mode. 2805 | * @param bool $multicall 2806 | * @return int 2807 | */ 2808 | public function getGameMode($multicall = false) 2809 | { 2810 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2811 | } 2812 | 2813 | /** 2814 | * Set a new chat time value (actually the duration of the podium). 2815 | * Only available to Admin. 2816 | * @param int $chatTime In milliseconds, 0: no podium displayed 2817 | * @param bool $multicall 2818 | * @return bool 2819 | * @throws InvalidArgumentException 2820 | */ 2821 | public function setChatTime($chatTime, $multicall = false) 2822 | { 2823 | if (!is_int($chatTime)) { 2824 | throw new InvalidArgumentException('chatTime = ' . print_r($chatTime, true)); 2825 | } 2826 | 2827 | return $this->execute(ucfirst(__FUNCTION__), [$chatTime], $multicall); 2828 | } 2829 | 2830 | /** 2831 | * Get the current and next chat time. 2832 | * @param bool $multicall 2833 | * @return int[] {int CurrentValue, int NextValue} 2834 | */ 2835 | public function getChatTime($multicall = false) 2836 | { 2837 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2838 | } 2839 | 2840 | /** 2841 | * Set a new finish timeout value for legacy laps and rounds based modes. 2842 | * Only available to Admin. 2843 | * Requires a map restart to be taken into account. 2844 | * @param int $timeout In milliseconds, 0: default, 1: adaptative to the map duration 2845 | * @param bool $multicall 2846 | * @return bool 2847 | * @throws InvalidArgumentException 2848 | */ 2849 | public function setFinishTimeout($timeout, $multicall = false) 2850 | { 2851 | if (!is_int($timeout)) { 2852 | throw new InvalidArgumentException('timeout = ' . print_r($timeout, true)); 2853 | } 2854 | 2855 | return $this->execute(ucfirst(__FUNCTION__), [$timeout], $multicall); 2856 | } 2857 | 2858 | /** 2859 | * Get the current and next FinishTimeout. 2860 | * @param bool $multicall 2861 | * @return int[] {int CurrentValue, int NextValue} 2862 | */ 2863 | public function getFinishTimeout($multicall = false) 2864 | { 2865 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2866 | } 2867 | 2868 | /** 2869 | * Set whether to enable the automatic warm-up phase in all modes. 2870 | * Only available to Admin. 2871 | * Requires a map restart to be taken into account. 2872 | * @param int $duration 0: disable, number of rounds in rounds based modes, number of times the gold medal time otherwise 2873 | * @param bool $multicall 2874 | * @return bool 2875 | * @throws InvalidArgumentException 2876 | */ 2877 | public function setAllWarmUpDuration($duration, $multicall = false) 2878 | { 2879 | if (!is_int($duration)) { 2880 | throw new InvalidArgumentException('duration = ' . print_r($duration, true)); 2881 | } 2882 | 2883 | return $this->execute(ucfirst(__FUNCTION__), [$duration], $multicall); 2884 | } 2885 | 2886 | /** 2887 | * Get whether the automatic warm-up phase is enabled in all modes. 2888 | * @param bool $multicall 2889 | * @return int[] {int CurrentValue, int NextValue} 2890 | */ 2891 | public function getAllWarmUpDuration($multicall = false) 2892 | { 2893 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2894 | } 2895 | 2896 | /** 2897 | * Set whether to disallow players to respawn. 2898 | * Only available to Admin. 2899 | * Requires a map restart to be taken into account. 2900 | * @param bool $disable 2901 | * @param bool $multicall 2902 | * @return bool 2903 | * @throws InvalidArgumentException 2904 | */ 2905 | public function setDisableRespawn($disable, $multicall = false) 2906 | { 2907 | if (!is_bool($disable)) { 2908 | throw new InvalidArgumentException('disable = ' . print_r($disable, true)); 2909 | } 2910 | 2911 | return $this->execute(ucfirst(__FUNCTION__), [$disable], $multicall); 2912 | } 2913 | 2914 | /** 2915 | * Get whether players are disallowed to respawn. 2916 | * @param bool $multicall 2917 | * @return bool[] {bool CurrentValue, bool NextValue} 2918 | */ 2919 | public function getDisableRespawn($multicall = false) 2920 | { 2921 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2922 | } 2923 | 2924 | /** 2925 | * Set whether to override the players preferences and always display all opponents. 2926 | * Only available to Admin. 2927 | * Requires a map restart to be taken into account. 2928 | * @param int $opponents 0: no override, 1: show all, else: minimum number of opponents 2929 | * @param bool $multicall 2930 | * @return bool 2931 | * @throws InvalidArgumentException 2932 | */ 2933 | public function setForceShowAllOpponents($opponents, $multicall = false) 2934 | { 2935 | if (!is_int($opponents)) { 2936 | throw new InvalidArgumentException('opponents = ' . print_r($opponents, true)); 2937 | } 2938 | 2939 | return $this->execute(ucfirst(__FUNCTION__), [$opponents], $multicall); 2940 | } 2941 | 2942 | /** 2943 | * Get whether players are forced to show all opponents. 2944 | * @param bool $multicall 2945 | * @return int[] {int CurrentValue, int NextValue} 2946 | */ 2947 | public function getForceShowAllOpponents($multicall = false) 2948 | { 2949 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2950 | } 2951 | 2952 | /** 2953 | * Set a new mode script name for script mode. 2954 | * Only available to Admin. 2955 | * Requires a map restart to be taken into account. 2956 | * @param string $script 2957 | * @param bool $multicall 2958 | * @return bool 2959 | * @throws InvalidArgumentException 2960 | */ 2961 | public function setScriptName($script, $multicall = false) 2962 | { 2963 | if (!is_string($script)) { 2964 | throw new InvalidArgumentException('script = ' . print_r($script, true)); 2965 | } 2966 | $script = $this->secureUtf8($script); 2967 | 2968 | return $this->execute(ucfirst(__FUNCTION__), [$script], $multicall); 2969 | } 2970 | 2971 | /** 2972 | * Get the current and next mode script name for script mode. 2973 | * @param bool $multicall 2974 | * @return string[] {string CurrentValue, string NextValue} 2975 | */ 2976 | public function getScriptName($multicall = false) 2977 | { 2978 | if ($multicall) { 2979 | return $this->execute(ucfirst(__FUNCTION__), [], [$this, 'stripBom']); 2980 | } 2981 | return $this->stripBom($this->execute(ucfirst(__FUNCTION__))); 2982 | } 2983 | 2984 | /** 2985 | * Returns the current map index in the selection, or -1 if the map is no longer in the selection. 2986 | * @param bool $multicall 2987 | * @return int 2988 | */ 2989 | public function getCurrentMapIndex($multicall = false): int 2990 | { 2991 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 2992 | } 2993 | 2994 | /** 2995 | * Returns the map index in the selection that will be played next (unless the current one is restarted...) 2996 | * @param bool $multicall 2997 | * @return int 2998 | */ 2999 | public function getNextMapIndex($multicall = false): int 3000 | { 3001 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3002 | } 3003 | 3004 | /** 3005 | * Sets the map index in the selection that will be played next (unless the current one is restarted...) 3006 | * @param int $index 3007 | * @param bool $multicall 3008 | * @return bool 3009 | */ 3010 | public function setNextMapIndex(int $index, $multicall = false) 3011 | { 3012 | return $this->execute(ucfirst(__FUNCTION__), [$index], $multicall); 3013 | } 3014 | 3015 | /** 3016 | * Sets the map in the selection that will be played next (unless the current one is restarted...) 3017 | * @param string $ident 3018 | * @param bool $multicall 3019 | * @return bool 3020 | */ 3021 | public function setNextMapIdent(string $ident, $multicall = false) 3022 | { 3023 | return $this->execute(ucfirst(__FUNCTION__), [$ident], $multicall); 3024 | } 3025 | 3026 | /** 3027 | * Immediately jumps to the map designated by the index in the selection. 3028 | * @param int $index 3029 | * @param bool $multicall 3030 | * @return bool 3031 | */ 3032 | public function jumpToMapIndex(int $index, $multicall = false) 3033 | { 3034 | return $this->execute(ucfirst(__FUNCTION__), [$index], $multicall); 3035 | } 3036 | 3037 | /** 3038 | * Immediately jumps to the map designated by its identifier (it must be in the selection). 3039 | * @param string $ident 3040 | * @param bool $multicall 3041 | * @return bool 3042 | */ 3043 | public function jumpToMapIdent(string $ident, $multicall = false) 3044 | { 3045 | return $this->execute(ucfirst(__FUNCTION__), [$ident], $multicall); 3046 | } 3047 | 3048 | /** 3049 | * Returns a struct containing the infos for the current map. 3050 | * @param bool $multicall 3051 | * @return Structures\Map 3052 | */ 3053 | public function getCurrentMapInfo($multicall = false) 3054 | { 3055 | if ($multicall) { 3056 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Map')); 3057 | } 3058 | return Structures\Map::fromArray($this->execute(ucfirst(__FUNCTION__))); 3059 | } 3060 | 3061 | /** 3062 | * Returns a struct containing the infos for the next map. 3063 | * @param bool $multicall 3064 | * @return Structures\Map 3065 | */ 3066 | public function getNextMapInfo($multicall = false) 3067 | { 3068 | if ($multicall) { 3069 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('Map')); 3070 | } 3071 | return Structures\Map::fromArray($this->execute(ucfirst(__FUNCTION__))); 3072 | } 3073 | 3074 | /** 3075 | * Returns a struct containing the infos for the map with the specified filename. 3076 | * @param string $filename Relative to the Maps path 3077 | * @param bool $multicall 3078 | * @return Structures\Map 3079 | * @throws InvalidArgumentException 3080 | */ 3081 | public function getMapInfo(string $filename, $multicall = false) 3082 | { 3083 | $filename = $this->secureUtf8($filename); 3084 | 3085 | if ($multicall) { 3086 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $this->structHandler('Map')); 3087 | } 3088 | return Structures\Map::fromArray($this->execute(ucfirst(__FUNCTION__), [$filename])); 3089 | } 3090 | 3091 | /** 3092 | * Returns a boolean if the map with the specified filename matches the current server settings. 3093 | * @param string $filename Relative to the Maps path 3094 | * @param bool $multicall 3095 | * @return bool 3096 | * @throws InvalidArgumentException 3097 | */ 3098 | public function checkMapForCurrentServerParams(string $filename, $multicall = false) 3099 | { 3100 | $filename = $this->secureUtf8($filename); 3101 | 3102 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3103 | } 3104 | 3105 | /** 3106 | * Returns a list of maps among the current selection of the server. 3107 | * @param int $length Maximum number of infos to be returned 3108 | * @param int $offset Starting index in the list 3109 | * @param bool $multicall 3110 | * @return Structures\Map[] 3111 | */ 3112 | public function getMapList(int $length = -1, int $offset = 0, $multicall = false) 3113 | { 3114 | if ($multicall) { 3115 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('Map', true)); 3116 | } 3117 | return Structures\Map::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 3118 | } 3119 | 3120 | /** 3121 | * Add the map with the specified filename at the end of the current selection. 3122 | * Only available to Admin. 3123 | * @param string $filename Relative to the Maps path 3124 | * @param bool $multicall 3125 | * @return bool 3126 | */ 3127 | public function addMap(string $filename, $multicall = false) 3128 | { 3129 | $filename = $this->secureUtf8($filename); 3130 | 3131 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3132 | } 3133 | 3134 | /** 3135 | * Add the list of maps with the specified filenames at the end of the current selection. 3136 | * Only available to Admin. 3137 | * @param string[] $filenames Relative to the Maps path 3138 | * @param bool $multicall 3139 | * @return int Number of maps actually added 3140 | * @throws InvalidArgumentException 3141 | */ 3142 | public function addMapList($filenames, $multicall = false) 3143 | { 3144 | if (!is_array($filenames)) { 3145 | throw new InvalidArgumentException('filenames = ' . print_r($filenames, true)); 3146 | } 3147 | $filenames = $this->secureUtf8($filenames); 3148 | 3149 | return $this->execute(ucfirst(__FUNCTION__), [$filenames], $multicall); 3150 | } 3151 | 3152 | /** 3153 | * Remove the map with the specified filename from the current selection. 3154 | * Only available to Admin. 3155 | * @param string $filename Relative to the Maps path 3156 | * @param bool $multicall 3157 | * @return bool 3158 | * @throws InvalidArgumentException 3159 | */ 3160 | public function removeMap($filename, $multicall = false) 3161 | { 3162 | if (!is_string($filename) || !strlen($filename)) { 3163 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3164 | } 3165 | $filename = $this->secureUtf8($filename); 3166 | 3167 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3168 | } 3169 | 3170 | /** 3171 | * Remove the list of maps with the specified filenames from the current selection. 3172 | * Only available to Admin. 3173 | * @param string[] $filenames Relative to the Maps path 3174 | * @param bool $multicall 3175 | * @return int Number of maps actually removed 3176 | * @throws InvalidArgumentException 3177 | */ 3178 | public function removeMapList($filenames, $multicall = false) 3179 | { 3180 | if (!is_array($filenames)) { 3181 | throw new InvalidArgumentException('filenames = ' . print_r($filenames, true)); 3182 | } 3183 | $filenames = $this->secureUtf8($filenames); 3184 | 3185 | return $this->execute(ucfirst(__FUNCTION__), [$filenames], $multicall); 3186 | } 3187 | 3188 | /** 3189 | * Insert the map with the specified filename after the current map. 3190 | * Only available to Admin. 3191 | * @param string $filename Relative to the Maps path 3192 | * @param bool $multicall 3193 | * @return bool 3194 | * @throws InvalidArgumentException 3195 | */ 3196 | public function insertMap($filename, $multicall = false) 3197 | { 3198 | if (!is_string($filename) || !strlen($filename)) { 3199 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3200 | } 3201 | $filename = $this->secureUtf8($filename); 3202 | 3203 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3204 | } 3205 | 3206 | /** 3207 | * Insert the list of maps with the specified filenames after the current map. 3208 | * Only available to Admin. 3209 | * @param string[] $filenames Relative to the Maps path 3210 | * @param bool $multicall 3211 | * @return int Number of maps actually inserted 3212 | * @throws InvalidArgumentException 3213 | */ 3214 | public function insertMapList($filenames, $multicall = false) 3215 | { 3216 | if (!is_array($filenames)) { 3217 | throw new InvalidArgumentException('filenames = ' . print_r($filenames, true)); 3218 | } 3219 | $filenames = $this->secureUtf8($filenames); 3220 | 3221 | return $this->execute(ucfirst(__FUNCTION__), [$filenames], $multicall); 3222 | } 3223 | 3224 | /** 3225 | * Set as next map the one with the specified filename, if it is present in the selection. 3226 | * Only available to Admin. 3227 | * @param string $filename Relative to the Maps path 3228 | * @param bool $multicall 3229 | * @return bool 3230 | * @throws InvalidArgumentException 3231 | */ 3232 | public function chooseNextMap($filename, $multicall = false) 3233 | { 3234 | if (!is_string($filename) || !strlen($filename)) { 3235 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3236 | } 3237 | $filename = $this->secureUtf8($filename); 3238 | 3239 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3240 | } 3241 | 3242 | /** 3243 | * Set as next maps the list of maps with the specified filenames, if they are present in the selection. 3244 | * Only available to Admin. 3245 | * @param string[] $filenames Relative to the Maps path 3246 | * @param bool $multicall 3247 | * @return int Number of maps actually chosen 3248 | * @throws InvalidArgumentException 3249 | */ 3250 | public function chooseNextMapList($filenames, $multicall = false) 3251 | { 3252 | if (!is_array($filenames)) { 3253 | throw new InvalidArgumentException('filenames = ' . print_r($filenames, true)); 3254 | } 3255 | $filenames = $this->secureUtf8($filenames); 3256 | 3257 | return $this->execute(ucfirst(__FUNCTION__), [$filenames], $multicall); 3258 | } 3259 | 3260 | /** 3261 | * Set a list of maps defined in the playlist with the specified filename as the current selection of the server, and load the gameinfos from the same file. 3262 | * Only available to Admin. 3263 | * @param string $filename Relative to the Maps path 3264 | * @param bool $multicall 3265 | * @return int Number of maps in the new list 3266 | * @throws InvalidArgumentException 3267 | */ 3268 | public function loadMatchSettings($filename, $multicall = false) 3269 | { 3270 | if (!is_string($filename) || !strlen($filename)) { 3271 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3272 | } 3273 | $filename = $this->secureUtf8($filename); 3274 | 3275 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3276 | } 3277 | 3278 | /** 3279 | * Add a list of maps defined in the playlist with the specified filename at the end of the current selection. 3280 | * Only available to Admin. 3281 | * @param string $filename Relative to the Maps path 3282 | * @param bool $multicall 3283 | * @return int Number of maps actually added 3284 | * @throws InvalidArgumentException 3285 | */ 3286 | public function appendPlaylistFromMatchSettings($filename, $multicall = false) 3287 | { 3288 | if (!is_string($filename) || !strlen($filename)) { 3289 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3290 | } 3291 | $filename = $this->secureUtf8($filename); 3292 | 3293 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3294 | } 3295 | 3296 | /** 3297 | * Save the current selection of map in the playlist with the specified filename, as well as the current gameinfos. 3298 | * Only available to Admin. 3299 | * @param string $filename Relative to the Maps path 3300 | * @param bool $multicall 3301 | * @return int Number of maps in the saved playlist 3302 | * @throws InvalidArgumentException 3303 | */ 3304 | public function saveMatchSettings($filename, $multicall = false) 3305 | { 3306 | if (!is_string($filename) || !strlen($filename)) { 3307 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3308 | } 3309 | $filename = $this->secureUtf8($filename); 3310 | 3311 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3312 | } 3313 | 3314 | /** 3315 | * Insert a list of maps defined in the playlist with the specified filename after the current map. 3316 | * Only available to Admin. 3317 | * @param string $filename Relative to the Maps path 3318 | * @param bool $multicall 3319 | * @return int Number of maps actually inserted 3320 | * @throws InvalidArgumentException 3321 | */ 3322 | public function insertPlaylistFromMatchSettings($filename, $multicall = false) 3323 | { 3324 | if (!is_string($filename) || !strlen($filename)) { 3325 | throw new InvalidArgumentException('filename = ' . print_r($filename, true)); 3326 | } 3327 | $filename = $this->secureUtf8($filename); 3328 | 3329 | return $this->execute(ucfirst(__FUNCTION__), [$filename], $multicall); 3330 | } 3331 | 3332 | /** 3333 | * Returns the list of players on the server. 3334 | * @param int $length Maximum number of infos to be returned 3335 | * @param int $offset Starting index in the list 3336 | * @param int $compatibility 0: united, 1: forever, 2: forever including servers 3337 | * @param bool $multicall 3338 | * @return Structures\PlayerInfo[] 3339 | * @throws InvalidArgumentException 3340 | */ 3341 | public function getPlayerList($length = -1, $offset = 0, $compatibility = 1, $multicall = false) 3342 | { 3343 | if (!is_int($length)) { 3344 | throw new InvalidArgumentException('length = ' . print_r($length, true)); 3345 | } 3346 | if (!is_int($offset)) { 3347 | throw new InvalidArgumentException('offset = ' . print_r($offset, true)); 3348 | } 3349 | if (!is_int($compatibility) || $compatibility < 0 || $compatibility > 2) { 3350 | throw new InvalidArgumentException('compatibility = ' . print_r($compatibility, true)); 3351 | } 3352 | 3353 | if ($multicall) { 3354 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset, $compatibility], $this->structHandler('PlayerInfo', true)); 3355 | } 3356 | return Structures\PlayerInfo::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset, $compatibility])); 3357 | } 3358 | 3359 | /** 3360 | * Returns a struct containing the infos on the player with the specified login. 3361 | * @param mixed $player Login or player object 3362 | * @param int $compatibility 0: united, 1: forever 3363 | * @param bool $multicall 3364 | * @return Structures\PlayerInfo 3365 | * @throws InvalidArgumentException 3366 | */ 3367 | public function getPlayerInfo($player, $compatibility = 1, $multicall = false) 3368 | { 3369 | $login = $this->getLogin($player); 3370 | if ($login === false) { 3371 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 3372 | } 3373 | if ($compatibility !== 0 && $compatibility !== 1) { 3374 | throw new InvalidArgumentException('compatibility = ' . print_r($compatibility, true)); 3375 | } 3376 | 3377 | if ($multicall) { 3378 | return $this->execute(ucfirst(__FUNCTION__), [$login, $compatibility], $this->structHandler('PlayerInfo')); 3379 | } 3380 | return Structures\PlayerInfo::fromArray($this->execute(ucfirst(__FUNCTION__), [$login, $compatibility])); 3381 | } 3382 | 3383 | /** 3384 | * Returns a struct containing the infos on the player with the specified login. 3385 | * @param mixed $player Login or player object 3386 | * @param bool $multicall 3387 | * @return Structures\PlayerDetailedInfo 3388 | * @throws InvalidArgumentException 3389 | */ 3390 | public function getDetailedPlayerInfo($player, $multicall = false) 3391 | { 3392 | $login = $this->getLogin($player); 3393 | if ($login === false) { 3394 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 3395 | } 3396 | 3397 | if ($multicall) { 3398 | return $this->execute(ucfirst(__FUNCTION__), [$login], $this->structHandler('PlayerDetailedInfo')); 3399 | } 3400 | return Structures\PlayerDetailedInfo::fromArray($this->execute(ucfirst(__FUNCTION__), [$login])); 3401 | } 3402 | 3403 | /** 3404 | * Returns a struct containing the player infos of the game server 3405 | * (ie: in case of a basic server, itself; in case of a relay server, the main server) 3406 | * @param int $compatibility 0: united, 1: forever 3407 | * @param bool $multicall 3408 | * @return Structures\PlayerInfo 3409 | * @throws InvalidArgumentException 3410 | */ 3411 | public function getMainServerPlayerInfo($compatibility = 1, $multicall = false) 3412 | { 3413 | if (!is_int($compatibility)) { 3414 | throw new InvalidArgumentException('compatibility = ' . print_r($compatibility, true)); 3415 | } 3416 | 3417 | if ($multicall) { 3418 | return $this->execute(ucfirst(__FUNCTION__), [$compatibility], $this->structHandler('PlayerInfo')); 3419 | } 3420 | return Structures\PlayerInfo::fromArray($this->execute(ucfirst(__FUNCTION__), [$compatibility])); 3421 | } 3422 | 3423 | /** 3424 | * Returns the current rankings for the match in progress. 3425 | * In script modes, scores aren't returned. 3426 | * In team modes, the scores for the two teams are returned. 3427 | * In other modes, it's the individual players' scores. 3428 | * @param int $length Maximum number of infos to be returned 3429 | * @param int $offset Starting index in the list 3430 | * @param bool $multicall 3431 | * @return Structures\PlayerRanking[] 3432 | * @throws InvalidArgumentException 3433 | */ 3434 | public function getCurrentRanking($length = -1, $offset = 0, $multicall = false) 3435 | { 3436 | if (!is_int($length)) { 3437 | throw new InvalidArgumentException('length = ' . print_r($length, true)); 3438 | } 3439 | if (!is_int($offset)) { 3440 | throw new InvalidArgumentException('offset = ' . print_r($offset, true)); 3441 | } 3442 | 3443 | if ($multicall) { 3444 | return $this->execute(ucfirst(__FUNCTION__), [$length, $offset], $this->structHandler('PlayerRanking', true)); 3445 | } 3446 | return Structures\PlayerRanking::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$length, $offset])); 3447 | } 3448 | 3449 | /** 3450 | * Returns the current ranking of the player with the specified login (or list of comma-separated logins) for the match in progress. 3451 | * In script modes, scores aren't returned. 3452 | * In other modes, it's the individual players' scores. 3453 | * @param mixed $players Login, player object or array 3454 | * @param bool $multicall 3455 | * @return Structures\PlayerRanking[] 3456 | * @throws InvalidArgumentException 3457 | */ 3458 | public function getCurrentRankingForLogin($players, $multicall = false) 3459 | { 3460 | $logins = $this->getLogins($players); 3461 | if ($logins === false) { 3462 | throw new InvalidArgumentException('players = ' . print_r($players, true)); 3463 | } 3464 | 3465 | if ($multicall) { 3466 | return $this->execute(ucfirst(__FUNCTION__), [$logins], $this->structHandler('PlayerRanking', true)); 3467 | } 3468 | return Structures\PlayerRanking::fromArrayOfArray($this->execute(ucfirst(__FUNCTION__), [$logins])); 3469 | } 3470 | 3471 | /** 3472 | * Returns the current winning team for the race in progress. 3473 | * @param bool $multicall 3474 | * @return int -1: if not in team mode or draw match, 0 or 1 otherwise 3475 | */ 3476 | public function getCurrentWinnerTeam($multicall = false) 3477 | { 3478 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3479 | } 3480 | 3481 | /** 3482 | * Force the scores of the current game. 3483 | * Only available in rounds and team mode. 3484 | * Only available to Admin/SuperAdmin. 3485 | * @param int[][] $scores Array of structs {int PlayerId, int Score} 3486 | * @param bool $silent True to update silently (only available for SuperAdmin) 3487 | * @param bool $multicall 3488 | * @return bool 3489 | * @throws InvalidArgumentException 3490 | */ 3491 | public function forceScores($scores, $silent, $multicall = false) 3492 | { 3493 | if (!is_array($scores)) { 3494 | throw new InvalidArgumentException('scores = ' . print_r($scores, true)); 3495 | } 3496 | foreach ($scores as $i => $score) { 3497 | if (!is_array($score)) { 3498 | throw new InvalidArgumentException('score[' . $i . '] = ' . print_r($score, true)); 3499 | } 3500 | if (!isset($score['PlayerId']) || !is_int($score['PlayerId'])) { 3501 | throw new InvalidArgumentException('score[' . $i . ']["PlayerId"] = ' . print_r($score, true)); 3502 | } 3503 | if (!isset($score['Score']) || !is_int($score['Score'])) { 3504 | throw new InvalidArgumentException('score[' . $i . ']["Score"] = ' . print_r($score, true)); 3505 | } 3506 | } 3507 | if (!is_bool($silent)) { 3508 | throw new InvalidArgumentException('silent = ' . print_r($silent, true)); 3509 | } 3510 | 3511 | return $this->execute(ucfirst(__FUNCTION__), [$scores, $silent], $multicall); 3512 | } 3513 | 3514 | /** 3515 | * Force the team of the player. 3516 | * Only available in team mode. 3517 | * Only available to Admin. 3518 | * @param mixed $player Login or player object 3519 | * @param int $team 0 or 1 3520 | * @param bool $multicall 3521 | * @return bool 3522 | * @throws InvalidArgumentException 3523 | */ 3524 | public function forcePlayerTeam($player, $team, $multicall = false) 3525 | { 3526 | $login = $this->getLogin($player); 3527 | if ($login === false) { 3528 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 3529 | } 3530 | if ($team !== 0 && $team !== 1) { 3531 | throw new InvalidArgumentException('team = ' . print_r($team, true)); 3532 | } 3533 | 3534 | return $this->execute(ucfirst(__FUNCTION__), [$login, $team], $multicall); 3535 | } 3536 | 3537 | /** 3538 | * Force the spectating status of the player. 3539 | * Only available to Admin. 3540 | * @param mixed $player Login or player object 3541 | * @param int $mode 0: user selectable, 1: spectator, 2: player, 3: spectator but keep selectable 3542 | * @param bool $multicall 3543 | * @return bool 3544 | * @throws InvalidArgumentException 3545 | */ 3546 | public function forceSpectator($player, $mode, $multicall = false) 3547 | { 3548 | $login = $this->getLogin($player); 3549 | if ($login === false) { 3550 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 3551 | } 3552 | if (!is_int($mode) || $mode < 0 || $mode > 3) { 3553 | throw new InvalidArgumentException('mode = ' . print_r($mode, true)); 3554 | } 3555 | 3556 | return $this->execute(ucfirst(__FUNCTION__), [$login, $mode], $multicall); 3557 | } 3558 | 3559 | /** 3560 | * Force spectators to look at a specific player. 3561 | * Only available to Admin. 3562 | * @param mixed $spectator Login or player object; empty for all 3563 | * @param mixed $target Login or player object; empty for automatic 3564 | * @param int $camera -1: leave unchanged, 0: replay, 1: follow, 2: free 3565 | * @param bool $multicall 3566 | * @return bool 3567 | * @throws InvalidArgumentException 3568 | */ 3569 | public function forceSpectatorTarget($spectator, $target, $camera, $multicall = false) 3570 | { 3571 | $spectatorLogin = $this->getLogin($spectator, true); 3572 | if ($spectatorLogin === false) { 3573 | throw new InvalidArgumentException('player = ' . print_r($spectator, true)); 3574 | } 3575 | $targetLogin = $this->getLogin($target, true); 3576 | if ($targetLogin === false) { 3577 | throw new InvalidArgumentException('target = ' . print_r($target, true)); 3578 | } 3579 | if (!is_int($camera) || $camera < -1 || $camera > 2) { 3580 | throw new InvalidArgumentException('camera = ' . print_r($camera, true)); 3581 | } 3582 | 3583 | return $this->execute(ucfirst(__FUNCTION__), [$spectatorLogin, $targetLogin, $camera], $multicall); 3584 | } 3585 | 3586 | /** 3587 | * Pass the login of the spectator. 3588 | * A spectator that once was a player keeps his player slot, so that he can go back to player mode. 3589 | * Calling this function frees this slot for another player to connect. 3590 | * Only available to Admin. 3591 | * @param mixed $player Login or player object 3592 | * @param bool $multicall 3593 | * @return bool 3594 | * @throws InvalidArgumentException 3595 | */ 3596 | public function spectatorReleasePlayerSlot($player, $multicall = false) 3597 | { 3598 | $login = $this->getLogin($player); 3599 | if ($login === false) { 3600 | throw new InvalidArgumentException('player = ' . print_r($player, true)); 3601 | } 3602 | 3603 | return $this->execute(ucfirst(__FUNCTION__), [$login], $multicall); 3604 | } 3605 | 3606 | /** 3607 | * Enable control of the game flow: the game will wait for the caller to validate state transitions. 3608 | * Only available to Admin. 3609 | * @param bool $enable 3610 | * @param bool $multicall 3611 | * @return bool 3612 | * @throws InvalidArgumentException 3613 | */ 3614 | public function manualFlowControlEnable($enable = true, $multicall = false) 3615 | { 3616 | if (!is_bool($enable)) { 3617 | throw new InvalidArgumentException('enable = ' . print_r($enable, true)); 3618 | } 3619 | 3620 | return $this->execute(ucfirst(__FUNCTION__), [$enable], $multicall); 3621 | } 3622 | 3623 | /** 3624 | * Allows the game to proceed. 3625 | * Only available to Admin. 3626 | * @param bool $multicall 3627 | * @return bool 3628 | */ 3629 | public function manualFlowControlProceed($multicall = false) 3630 | { 3631 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3632 | } 3633 | 3634 | /** 3635 | * Returns whether the manual control of the game flow is enabled. 3636 | * Only available to Admin. 3637 | * @param bool $multicall 3638 | * @return int 0: no, 1: yes by the xml-rpc client making the call, 2: yes by some other xml-rpc client 3639 | */ 3640 | public function manualFlowControlIsEnabled($multicall = false) 3641 | { 3642 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3643 | } 3644 | 3645 | /** 3646 | * Returns the transition that is currently blocked, or '' if none. 3647 | * (That's exactly the value last received by the callback.) 3648 | * Only available to Admin. 3649 | * @param bool $multicall 3650 | * @return string 3651 | */ 3652 | public function manualFlowControlGetCurTransition($multicall = false) 3653 | { 3654 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3655 | } 3656 | 3657 | /** 3658 | * Returns the current match ending condition. 3659 | * @param bool $multicall 3660 | * @return string 'Playing', 'ChangeMap' or 'Finished' 3661 | */ 3662 | public function checkEndMatchCondition($multicall = false) 3663 | { 3664 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3665 | } 3666 | 3667 | /** 3668 | * Returns a struct containing the networks stats of the server. 3669 | * Only available to SuperAdmin. 3670 | * @param bool $multicall 3671 | * @return Structures\NetworkStats 3672 | */ 3673 | public function getNetworkStats($multicall = false) 3674 | { 3675 | if ($multicall) { 3676 | return $this->execute(ucfirst(__FUNCTION__), [], $this->structHandler('NetworkStats')); 3677 | } 3678 | return Structures\NetworkStats::fromArray($this->execute(ucfirst(__FUNCTION__))); 3679 | } 3680 | 3681 | /** 3682 | * Start a server on lan, using the current configuration. 3683 | * Only available to SuperAdmin. 3684 | * @param bool $multicall 3685 | * @return bool 3686 | */ 3687 | public function startServerLan($multicall = false) 3688 | { 3689 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3690 | } 3691 | 3692 | /** 3693 | * Start a server on internet, using the current configuration. 3694 | * Only available to SuperAdmin. 3695 | * @param bool $multicall 3696 | * @return bool 3697 | */ 3698 | public function startServerInternet($multicall = false) 3699 | { 3700 | return $this->execute(ucfirst(__FUNCTION__), [], $multicall); 3701 | } 3702 | 3703 | /** 3704 | * Join the server on lan. 3705 | * Only available on client. 3706 | * Only available to Admin. 3707 | * @param string $host IPv4 with optionally a port (eg. '192.168.1.42:2350') 3708 | * @param string $password 3709 | * @param bool $multicall 3710 | * @return bool 3711 | * @throws InvalidArgumentException 3712 | */ 3713 | public function joinServerLan($host, $password = '', $multicall = false) 3714 | { 3715 | if (!is_string($host)) { 3716 | throw new InvalidArgumentException('host = ' . print_r($host, true)); 3717 | } 3718 | if (!is_string($password)) { 3719 | throw new InvalidArgumentException('password = ' . print_r($password, true)); 3720 | } 3721 | 3722 | return $this->execute(ucfirst(__FUNCTION__), [['Server' => $host, 'ServerPassword' => $password]], $multicall); 3723 | } 3724 | 3725 | /** 3726 | * Join the server on internet. 3727 | * Only available on client. 3728 | * Only available to Admin. 3729 | * @param string $host Server login or IPv4 with optionally a port (eg. '192.168.1.42:2350') 3730 | * @param string $password 3731 | * @param bool $multicall 3732 | * @return bool 3733 | * @throws InvalidArgumentException 3734 | */ 3735 | public function joinServerInternet($host, $password = '', $multicall = false) 3736 | { 3737 | if (!is_string($host)) { 3738 | throw new InvalidArgumentException('host = ' . print_r($host, true)); 3739 | } 3740 | if (!is_string($password)) { 3741 | throw new InvalidArgumentException('password = ' . print_r($password, true)); 3742 | } 3743 | 3744 | return $this->execute(ucfirst(__FUNCTION__), [['Server' => $host, 'ServerPassword' => $password]], $multicall); 3745 | } 3746 | } 3747 | 3748 | /** 3749 | * Exception Dedicated to Invalid Argument Error on Request Call 3750 | */ 3751 | class InvalidArgumentException extends \Exception 3752 | { 3753 | } 3754 | --------------------------------------------------------------------------------