├── .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 | [](https://packagist.org/packages/maniaplanet/dedicated-server-api)
5 | [](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 |
--------------------------------------------------------------------------------