cherry pick maniaplanet lib 6.1
This commit is contained in:
		| @@ -299,7 +299,6 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, | ||||
| 			} | ||||
|  | ||||
| 			// Delete client | ||||
| 			Connection::delete($this->getClient()); | ||||
| 			$this->client = null; | ||||
| 		} | ||||
|  | ||||
| @@ -722,7 +721,7 @@ class ManiaControl implements CallbackListener, CommandListener, TimerListener, | ||||
| 		Logger::log("Connecting to Server at {$serverConfig->host}:{$serverConfig->port}..."); | ||||
|  | ||||
| 		try { | ||||
| 			$this->client = Connection::factory($serverConfig->host, $serverConfig->port, self::SCRIPT_TIMEOUT, $serverConfig->user, $serverConfig->pass, self::API_VERSION); | ||||
| 			$this->client = new Connection($serverConfig->host, $serverConfig->port, self::SCRIPT_TIMEOUT, $serverConfig->user, $serverConfig->pass, self::API_VERSION); | ||||
| 		} catch (TransportException $exception) { | ||||
| 			$message = "Couldn't connect to the server: '{$exception->getMessage()}'"; | ||||
| 			$this->quit($message, true); | ||||
|   | ||||
							
								
								
									
										2272
									
								
								libs/Maniaplanet/DedicatedServer/Connection.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2272
									
								
								libs/Maniaplanet/DedicatedServer/Connection.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,46 +9,52 @@ namespace Maniaplanet\DedicatedServer\Structures; | ||||
|  | ||||
| abstract class AbstractStructure | ||||
| { | ||||
| 	static public function fromArray($array) | ||||
| 	public static function fromArrayOfArray($array) | ||||
| 	{ | ||||
| 		if(!is_array($array)) | ||||
| 		if (!is_array($array)) { | ||||
| 			return $array; | ||||
|  | ||||
| 		$object = new static; | ||||
| 		foreach($array as $key => $value) | ||||
| 			$object->{lcfirst($key)} = $value; | ||||
| 		return $object; | ||||
| 		} | ||||
|  | ||||
| 	static public function fromArrayOfArray($array) | ||||
| 	{ | ||||
| 		if(!is_array($array)) | ||||
| 			return $array; | ||||
|  | ||||
| 		$result = array(); | ||||
| 		foreach($array as $key => $value) | ||||
| 		$result = []; | ||||
| 		foreach ($array as $key => $value) { | ||||
| 			$result[$key] = static::fromArray($value); | ||||
| 		} | ||||
| 		return $result; | ||||
| 	} | ||||
|  | ||||
| 	static public function getPropertyFromArray($array, $property) | ||||
| 	public static function fromArray($array) | ||||
| 	{ | ||||
| 		if (!is_array($array)) { | ||||
| 			return $array; | ||||
| 		} | ||||
|  | ||||
| 		$object = new static; | ||||
| 		foreach ($array as $key => $value) { | ||||
| 			$object->{lcfirst($key)} = $value; | ||||
| 		} | ||||
| 		return $object; | ||||
| 	} | ||||
|  | ||||
| 	public static function getPropertyFromArray($array, $property) | ||||
| 	{ | ||||
| 		return array_map(get_called_class() . '::extractProperty', $array, array_fill(0, count($array), $property)); | ||||
| 	} | ||||
|  | ||||
| 	static protected function extractProperty($element, $property) | ||||
| 	protected static function extractProperty($element, $property) | ||||
| 	{ | ||||
| 		if(!is_a($element, get_called_class()) || !property_exists($element, $property)) | ||||
| 		if (!is_a($element, get_called_class()) || !property_exists($element, $property)) { | ||||
| 			throw new \InvalidArgumentException('property ' . $property . ' does not exists in class: ' . get_called_class()); | ||||
| 		} | ||||
|  | ||||
| 		return $element->$property; | ||||
| 	} | ||||
|  | ||||
| 	function toArray() | ||||
| 	{ | ||||
| 		$out = array(); | ||||
| 		foreach(get_object_vars($this) as $key => $value) | ||||
| 		$out = []; | ||||
| 		foreach (get_object_vars($this) as $key => $value) { | ||||
| 			$out[ucfirst($key)] = $value; | ||||
| 		} | ||||
| 		return $out; | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class NetworkStats extends AbstractStructure | ||||
| 	/** @var PlayerNetInfo[] */ | ||||
| 	public $playerNetInfos; | ||||
|  | ||||
| 	static public function fromArray($array) | ||||
| 	public static function fromArray($array) | ||||
| 	{ | ||||
| 		$object = parent::fromArray($array); | ||||
| 		$object->playerNetInfos = PlayerNetInfo::fromArrayOfArray($object->playerNetInfos); | ||||
|   | ||||
							
								
								
									
										1
									
								
								libs/Maniaplanet/DedicatedServer/Structures/Player.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										1
									
								
								libs/Maniaplanet/DedicatedServer/Structures/Player.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -4,6 +4,7 @@ | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Structures; | ||||
|  | ||||
| class Player extends AbstractStructure | ||||
|   | ||||
| @@ -46,10 +46,22 @@ class PlayerDetailedInfo extends Player | ||||
| 	/** @var string */ | ||||
| 	public $broadcasterLogin; | ||||
| 	/** @var string[] */ | ||||
| 	public $allies = array(); | ||||
| 	public $allies = []; | ||||
| 	/** @var string */ | ||||
| 	public $clubLink; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return PlayerDetailedInfo | ||||
| 	 */ | ||||
| 	public static function fromArray($array) | ||||
| 	{ | ||||
| 		$object = parent::fromArray($array); | ||||
| 		$object->avatar = FileDesc::fromArray($object->avatar); | ||||
| 		$object->skins = Skin::fromArrayOfArray($object->skins); | ||||
| 		$object->ladderStats = LadderStats::fromArray($object->ladderStats); | ||||
| 		return $object; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return string[] | ||||
| 	 */ | ||||
| @@ -57,16 +69,4 @@ class PlayerDetailedInfo extends Player | ||||
| 	{ | ||||
| 		return explode('|', $this->path); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return PlayerDetailedInfo | ||||
| 	 */ | ||||
| 	static public function fromArray($array) | ||||
| 	{ | ||||
| 		$object = parent::fromArray($array); | ||||
| 		$object->avatar = FileDesc::fromArray($object->avatar); | ||||
| 		$object->skins = Skin::fromArrayOfArray($object->skins); | ||||
| 		$object->ladderStats = LadderStats::fromArray($object->ladderStats); | ||||
| 		return $object; | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										4
									
								
								libs/Maniaplanet/DedicatedServer/Structures/PlayerInfo.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										4
									
								
								libs/Maniaplanet/DedicatedServer/Structures/PlayerInfo.php
									
									
									
									
									
										
										
										Executable file → Normal file
									
								
							| @@ -20,7 +20,7 @@ class PlayerInfo extends Player | ||||
| 	/** @var bool */ | ||||
| 	public $isInOfficialMode; | ||||
| 	/** @var int */ | ||||
| 	public $ladderScore; | ||||
| 	public $ladderScore; // TODO CHECK IF EXISTS | ||||
| 	/** @var int */ | ||||
| 	public $ladderRanking; | ||||
| 	/** @var int */ | ||||
| @@ -63,7 +63,7 @@ class PlayerInfo extends Player | ||||
| 	/** | ||||
| 	 * @return PlayerInfo | ||||
| 	 */ | ||||
| 	static public function fromArray($array) | ||||
| 	public static function fromArray($array) | ||||
| 	{ | ||||
| 		$object = parent::fromArray($array); | ||||
|  | ||||
|   | ||||
| @@ -18,9 +18,9 @@ class ScriptInfo extends AbstractStructure | ||||
| 	/** @var string */ | ||||
| 	public $version; | ||||
| 	/** @var ScriptSettings[] */ | ||||
| 	public $paramDescs = array(); | ||||
| 	public $paramDescs = []; | ||||
| 	/** @var Command[] */ | ||||
| 	public $commandDescs = array(); | ||||
| 	public $commandDescs = []; | ||||
|  | ||||
| 	/** | ||||
| 	 * @return ScriptInfo | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class ServerOptions extends AbstractStructure | ||||
| 	 * @internal | ||||
| 	 * @return bool | ||||
| 	 */ | ||||
| 	function isValid() | ||||
| 	public function isValid(): bool | ||||
| 	{ | ||||
| 		return is_string($this->name) | ||||
| 			&& is_string($this->comment) | ||||
| @@ -41,15 +41,16 @@ class ServerOptions extends AbstractStructure | ||||
| 	 * @internal | ||||
| 	 * @return mixed[] | ||||
| 	 */ | ||||
| 	function toSetterArray() | ||||
| 	public function toSetterArray() | ||||
| 	{ | ||||
| 		$out = array(); | ||||
| 		foreach(get_object_vars($this) as $key => $value) | ||||
| 		{ | ||||
| 			if(substr($key, 0, 7) == 'current' || $value === null) | ||||
| 		$out = []; | ||||
| 		foreach (get_object_vars($this) as $key => $value) { | ||||
| 			if (str_starts_with($key, 'current') || $value === null) { | ||||
| 				continue; | ||||
| 			if($key == 'nextUseChangingValidationSeed') | ||||
| 			} | ||||
| 			if ($key === 'nextUseChangingValidationSeed') { | ||||
| 				$key = 'useChangingValidationSeed'; | ||||
| 			} | ||||
| 			$out[ucfirst($key)] = $value; | ||||
| 		} | ||||
| 		return $out; | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class Vote extends AbstractStructure | ||||
| 	 * @param string $cmdName | ||||
| 	 * @param mixed[] $cmdParam | ||||
| 	 */ | ||||
| 	function __construct($cmdName='', $cmdParam=array()) | ||||
| 	function __construct($cmdName = '', $cmdParam = []) | ||||
| 	{ | ||||
| 		$this->cmdName = $cmdName; | ||||
| 		$this->cmdParam = $cmdParam; | ||||
|   | ||||
| @@ -7,4 +7,6 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Exception extends \Exception {} | ||||
| class Exception extends \Exception | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -7,8 +7,10 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class FaultException extends Exception { | ||||
| 	static function create($faultString, $faultCode) { | ||||
| class FaultException extends Exception | ||||
| { | ||||
| 	static function create($faultString, $faultCode) | ||||
| 	{ | ||||
| 		switch ($faultString) { | ||||
| 			case 'Password incorrect.': | ||||
| 			case 'Permission denied.': | ||||
| @@ -31,6 +33,7 @@ class FaultException extends Exception { | ||||
| 			case 'Already waiting for a vote.': | ||||
| 			case 'You must stop server first.': | ||||
| 				return new LockedFeatureException($faultString, $faultCode); | ||||
| 			case 'Can\'t kick server.': | ||||
| 			case 'Login or Uid unknown.': | ||||
| 			case 'Login unknown.': | ||||
| 			case 'Payer login unknown.': | ||||
| @@ -110,44 +113,58 @@ class FaultException extends Exception { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class AuthenticationException extends FaultException { | ||||
| class AuthenticationException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class UnavailableFeatureException extends FaultException { | ||||
| class UnavailableFeatureException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class LockedFeatureException extends FaultException { | ||||
| class LockedFeatureException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class UnknownPlayerException extends FaultException { | ||||
| class UnknownPlayerException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class PlayerStateException extends FaultException { | ||||
| class PlayerStateException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class AlreadyInListException extends FaultException { | ||||
| class AlreadyInListException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class NotInListException extends FaultException { | ||||
| class NotInListException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class IndexOutOfBoundException extends FaultException { | ||||
| class IndexOutOfBoundException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class NextMapException extends FaultException { | ||||
| class NextMapException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class ChangeInProgressException extends FaultException { | ||||
| class ChangeInProgressException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class InvalidMapException extends FaultException { | ||||
| class InvalidMapException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class GameModeException extends FaultException { | ||||
| class GameModeException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class ServerOptionsException extends FaultException { | ||||
| class ServerOptionsException extends FaultException | ||||
| { | ||||
| } | ||||
|  | ||||
| class FileException extends FaultException { | ||||
| class FileException extends FaultException | ||||
| { | ||||
| } | ||||
|   | ||||
| @@ -9,18 +9,18 @@ namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class GbxRemote | ||||
| { | ||||
| 	const MAX_REQUEST_SIZE  = 0x200000; // 2MB | ||||
| 	const MAX_REQUEST_SIZE = 0x400000; // 4MB | ||||
| 	const MAX_RESPONSE_SIZE = 0x400000; // 4MB | ||||
|  | ||||
| 	public static $received; | ||||
| 	public static $sent; | ||||
|  | ||||
| 	private $socket; | ||||
| 	private $readTimeout = array('sec' => 30, 'usec' => 0); | ||||
| 	private $writeTimeout = array('sec' => 30, 'usec' => 0); | ||||
| 	private $readTimeout = ['sec' => 5, 'usec' => 0]; | ||||
| 	private $writeTimeout = ['sec' => 5, 'usec' => 0]; | ||||
| 	private $requestHandle; | ||||
| 	private $callbacksBuffer = array(); | ||||
| 	private $multicallBuffer = array(); | ||||
| 	private $callbacksBuffer = []; | ||||
| 	private $multicallBuffer = []; | ||||
| 	private $lastNetworkActivity = 0; | ||||
|  | ||||
| 	/** | ||||
| @@ -34,25 +34,99 @@ class GbxRemote | ||||
| 		$this->connect($host, $port, $timeout); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| 	 * @param int $timeout | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function connect($host, $port, $timeout) | ||||
| 	{ | ||||
| 		$this->socket = @fsockopen($host, $port, $errno, $errstr, $timeout); | ||||
| 		if (!$this->socket) { | ||||
| 			throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); | ||||
| 		} | ||||
|  | ||||
| 		stream_set_read_buffer($this->socket, 0); | ||||
| 		stream_set_write_buffer($this->socket, 0); | ||||
|  | ||||
| 		// handshake | ||||
| 		$header = $this->read(15); | ||||
| 		if ($header === false) { | ||||
| 			if (!is_resource($this->socket)) { | ||||
| 				$this->onIoFailure('socket closed during handshake'); | ||||
| 			} | ||||
| 			$this->onIoFailure(sprintf('during handshake (%s)', socket_strerror(socket_last_error()))); | ||||
| 		} | ||||
|  | ||||
| 		extract(unpack('Vsize/a*protocol', $header)); | ||||
| 		/** @var $size int */ | ||||
| 		/** @var $protocol string */ | ||||
| 		if ($size != 11 || $protocol != 'GBXRemote 2') { | ||||
| 			throw new TransportException('Wrong protocol header', TransportException::WRONG_PROTOCOL); | ||||
| 		} | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param int $size | ||||
| 	 * @return boolean|string | ||||
| 	 */ | ||||
| 	private function read($size) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, $this->readTimeout['sec'], $this->readTimeout['usec']); | ||||
|  | ||||
| 		$data = ''; | ||||
| 		while (strlen($data) < $size) { | ||||
| 			$buf = @fread($this->socket, $size - strlen($data)); | ||||
| 			if ($buf === '' || $buf === false) { | ||||
| 				return false; | ||||
| 			} | ||||
| 			$data .= $buf; | ||||
| 		} | ||||
|  | ||||
| 		self::$received += $size; | ||||
| 		return $data; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $when | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function onIoFailure($when) | ||||
| 	{ | ||||
| 		$meta = stream_get_meta_data($this->socket); | ||||
| 		if ($meta['timed_out']) { | ||||
| 			throw new TransportException('Connection timed out ' . $when, TransportException::TIMED_OUT); | ||||
| 		} | ||||
| 		throw new TransportException('Connection interrupted ' . $when, TransportException::INTERRUPTED); | ||||
| 	} | ||||
|  | ||||
| 	function __destruct() | ||||
| 	{ | ||||
| 		$this->terminate(); | ||||
| 	} | ||||
|  | ||||
| 	public function terminate() | ||||
| 	{ | ||||
| 		if ($this->socket) { | ||||
| 			fclose($this->socket); | ||||
| 			$this->socket = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Change timeouts | ||||
| 	 * @param int $read read timeout (in ms), 0 to leave unchanged | ||||
| 	 * @param int $write write timeout (in ms), 0 to leave unchanged | ||||
| 	 */ | ||||
| 	function setTimeouts($read=0, $write=0) | ||||
| 	{ | ||||
| 		if($read) | ||||
| 	public function setTimeouts($read = 0, $write = 0) | ||||
| 	{ | ||||
| 		if ($read) { | ||||
| 			$this->readTimeout['sec'] = (int)($read / 1000); | ||||
| 			$this->readTimeout['usec'] = ($read % 1000) * 1000; | ||||
| 		} | ||||
| 		if($write) | ||||
| 		{ | ||||
| 		if ($write) { | ||||
| 			$this->writeTimeout['sec'] = (int)($write / 1000); | ||||
| 			$this->writeTimeout['usec'] = ($write % 1000) * 1000; | ||||
| 		} | ||||
| @@ -68,39 +142,49 @@ class GbxRemote | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| 	 * @param int $timeout | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function connect($host, $port, $timeout) | ||||
| 	private function assertConnected() | ||||
| 	{ | ||||
| 		$this->socket = @fsockopen($host, $port, $errno, $errstr, $timeout); | ||||
| 		if(!$this->socket) | ||||
| 			throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); | ||||
|  | ||||
| 		stream_set_read_buffer($this->socket, 0); | ||||
| 		stream_set_write_buffer($this->socket, 0); | ||||
|  | ||||
| 		// handshake | ||||
| 		$header = $this->read(15); | ||||
| 		if($header === false) | ||||
| 			$this->onIoFailure(sprintf('during handshake (%s)', socket_strerror(socket_last_error($this->socket)))); | ||||
|  | ||||
| 		extract(unpack('Vsize/a*protocol', $header)); | ||||
| 		/** @var $size int */ | ||||
| 		/** @var $protocol string */ | ||||
| 		if($size != 11 || $protocol != 'GBXRemote 2') | ||||
| 			throw new TransportException('Wrong protocol header', TransportException::WRONG_PROTOCOL); | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 		if (!$this->socket) { | ||||
| 			throw new TransportException('Connection not initialized', TransportException::NOT_INITIALIZED); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function terminate() | ||||
| 	/** | ||||
| 	 * @param string $method | ||||
| 	 * @param mixed[] $args | ||||
| 	 */ | ||||
| 	function addCall($method, $args) | ||||
| 	{ | ||||
| 		if($this->socket) | ||||
| 		$this->multicallBuffer[] = [ | ||||
| 			'methodName' => $method, | ||||
| 			'params' => $args | ||||
| 		]; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed | ||||
| 	 */ | ||||
| 	function multiquery() | ||||
| 	{ | ||||
| 			fclose($this->socket); | ||||
| 			$this->socket = null; | ||||
| 		switch (count($this->multicallBuffer)) { | ||||
| 			case 0: | ||||
| 				return []; | ||||
| 			case 1: | ||||
| 				$call = array_shift($this->multicallBuffer); | ||||
| 				return [$this->query($call['methodName'], $call['params'])]; | ||||
| 			default: | ||||
| 				$result = $this->query('system.multicall', [$this->multicallBuffer]); | ||||
| 				foreach ($result as &$value) { | ||||
| 					if (isset($value['faultCode'])) { | ||||
| 						$value = FaultException::create($value['faultString'], $value['faultCode']); | ||||
| 					} else { | ||||
| 						$value = $value[0]; | ||||
| 					} | ||||
| 				} | ||||
| 				$this->multicallBuffer = []; | ||||
| 				return $result; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -110,19 +194,19 @@ class GbxRemote | ||||
| 	 * @return mixed | ||||
| 	 * @throws MessageException | ||||
| 	 */ | ||||
| 	function query($method, $args=array()) | ||||
| 	function query($method, $args = []) | ||||
| 	{ | ||||
| 		$this->assertConnected(); | ||||
| 		$xml = Request::encode($method, $args); | ||||
|  | ||||
| 		if(strlen($xml) > self::MAX_REQUEST_SIZE-8) | ||||
| 		{ | ||||
| 			if($method != 'system.multicall' || count($args[0]) < 2) | ||||
| 		if (strlen($xml) > self::MAX_REQUEST_SIZE - 8) { | ||||
| 			if ($method != 'system.multicall' || count($args[0]) < 2) { | ||||
| 				throw new MessageException('Request too large', MessageException::REQUEST_TOO_LARGE); | ||||
| 			} | ||||
|  | ||||
| 			$mid = count($args[0]) >> 1; | ||||
| 			$res1 = $this->query('system.multicall', array(array_slice($args[0], 0, $mid))); | ||||
| 			$res2 = $this->query('system.multicall', array(array_slice($args[0], $mid))); | ||||
| 			$res1 = $this->query('system.multicall', [array_slice($args[0], 0, $mid)]); | ||||
| 			$res2 = $this->query('system.multicall', [array_slice($args[0], $mid)]); | ||||
| 			return array_merge($res1, $res2); | ||||
| 		} | ||||
|  | ||||
| @@ -131,60 +215,40 @@ class GbxRemote | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $method | ||||
| 	 * @param mixed[] $args | ||||
| 	 */ | ||||
| 	function addCall($method, $args) | ||||
| 	{ | ||||
| 		$this->multicallBuffer[] = array( | ||||
| 			'methodName' => $method, | ||||
| 			'params' => $args | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed | ||||
| 	 */ | ||||
| 	function multiquery() | ||||
| 	{ | ||||
| 		switch(count($this->multicallBuffer)) | ||||
| 		{ | ||||
| 			case 0: | ||||
| 				return array(); | ||||
| 			case 1: | ||||
| 				$call = array_shift($this->multicallBuffer); | ||||
| 				return array($this->query($call['methodName'], $call['params'])); | ||||
| 			default: | ||||
| 				$result = $this->query('system.multicall', array($this->multicallBuffer)); | ||||
| 				foreach($result as &$value) | ||||
| 					if(isset($value['faultCode'])) | ||||
| 						$value = FaultException::create($value['faultString'], $value['faultCode']); | ||||
| 					else | ||||
| 						$value = $value[0]; | ||||
| 				$this->multicallBuffer = array(); | ||||
| 				return $result; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed[] | ||||
| 	 */ | ||||
| 	function getCallbacks() | ||||
| 	{ | ||||
| 		$this->assertConnected(); | ||||
| 		$this->flush(); | ||||
| 		$cb = $this->callbacksBuffer; | ||||
| 		$this->callbacksBuffer = array(); | ||||
| 		return $cb; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $xml | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function assertConnected() | ||||
| 	private function writeMessage($xml) | ||||
| 	{ | ||||
| 		if(!$this->socket) | ||||
| 			throw new TransportException('Connection not initialized', TransportException::NOT_INITIALIZED); | ||||
| 		if ($this->requestHandle == (int)0xffffffff) { | ||||
| 			$this->requestHandle = (int)0x80000000; | ||||
| 		} | ||||
| 		$data = pack('V2', strlen($xml), ++$this->requestHandle) . $xml; | ||||
| 		if (!$this->write($data)) { | ||||
| 			$this->onIoFailure('while writing'); | ||||
| 		} | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $data | ||||
| 	 * @return boolean | ||||
| 	 */ | ||||
| 	private function write($data) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, $this->writeTimeout['sec'], $this->writeTimeout['usec']); | ||||
| 		self::$sent += strlen($data); | ||||
|  | ||||
| 		while (strlen($data) > 0) { | ||||
| 			$written = @fwrite($this->socket, $data); | ||||
| 			if ($written === 0 || $written === false) { | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			$data = substr($data, $written); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -194,18 +258,17 @@ class GbxRemote | ||||
| 	 */ | ||||
| 	private function flush($waitResponse = false) | ||||
| 	{ | ||||
| 		$r = array($this->socket); | ||||
| 		while($waitResponse || @stream_select($r, $w, $e, 0) > 0) | ||||
| 		{ | ||||
| 		$r = [$this->socket]; | ||||
| 		while ($waitResponse || @stream_select($r, $w, $e, 0) > 0) { | ||||
| 			list($handle, $xml) = $this->readMessage(); | ||||
| 			list($type, $value) = Request::decode($xml); | ||||
| 			switch($type) | ||||
| 			{ | ||||
| 			switch ($type) { | ||||
| 				case 'fault': | ||||
| 					throw FaultException::create($value['faultString'], $value['faultCode']); | ||||
| 				case 'response': | ||||
| 					if($handle == $this->requestHandle) | ||||
| 					if ($handle == $this->requestHandle) { | ||||
| 						return $value; | ||||
| 					} | ||||
| 					break; | ||||
| 				case 'call': | ||||
| 					$this->callbacksBuffer[] = $value; | ||||
| @@ -221,92 +284,40 @@ class GbxRemote | ||||
| 	private function readMessage() | ||||
| 	{ | ||||
| 		$header = $this->read(8); | ||||
| 		if($header === false) | ||||
| 		if ($header === false) { | ||||
| 			$this->onIoFailure('while reading header'); | ||||
| 		} | ||||
|  | ||||
| 		extract(unpack('Vsize/Vhandle', $header)); | ||||
| 		/** @var $size int */ | ||||
| 		/** @var $handle int */ | ||||
| 		if($size == 0 || $handle == 0) | ||||
| 		if ($size == 0 || $handle == 0) { | ||||
| 			throw new TransportException('Incorrect header', TransportException::PROTOCOL_ERROR); | ||||
| 		} | ||||
|  | ||||
| 		if($size > self::MAX_RESPONSE_SIZE) | ||||
| 		if ($size > self::MAX_RESPONSE_SIZE) { | ||||
| 			throw new MessageException('Response too large', MessageException::RESPONSE_TOO_LARGE); | ||||
| 		} | ||||
|  | ||||
| 		$data = $this->read($size); | ||||
| 		if($data === false) | ||||
| 		if ($data === false) { | ||||
| 			$this->onIoFailure('while reading data'); | ||||
| 		} | ||||
|  | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 		return array($handle, $data); | ||||
| 		return [$handle, $data]; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $xml | ||||
| 	 * @throws TransportException | ||||
| 	 * @return mixed[] | ||||
| 	 */ | ||||
| 	private function writeMessage($xml) | ||||
| 	function getCallbacks() | ||||
| 	{ | ||||
| 		if($this->requestHandle == (int) 0xffffffff) | ||||
| 			$this->requestHandle = (int) 0x80000000; | ||||
| 		$data = pack('V2', strlen($xml), ++$this->requestHandle).$xml; | ||||
| 		if(!$this->write($data)) | ||||
| 			$this->onIoFailure('while writing'); | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param int $size | ||||
| 	 * @return boolean|string | ||||
| 	 */ | ||||
| 	private function read($size) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, $this->readTimeout['sec'], $this->readTimeout['usec']); | ||||
|  | ||||
| 		$data = ''; | ||||
| 		while(strlen($data) < $size) | ||||
| 		{ | ||||
| 			$buf = @fread($this->socket, $size - strlen($data)); | ||||
| 			if($buf === '' || $buf === false) | ||||
| 				return false; | ||||
| 			$data .= $buf; | ||||
| 		} | ||||
|  | ||||
| 		self::$received += $size; | ||||
| 		return $data; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $data | ||||
| 	 * @return boolean | ||||
| 	 */ | ||||
| 	private function write($data) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, $this->writeTimeout['sec'], $this->writeTimeout['usec']); | ||||
| 		self::$sent += strlen($data); | ||||
|  | ||||
| 		while(strlen($data) > 0) | ||||
| 		{ | ||||
| 			$written = @fwrite($this->socket, $data); | ||||
| 			if($written === 0 || $written === false) | ||||
| 				return false; | ||||
|  | ||||
| 			$data = substr($data, $written); | ||||
| 		} | ||||
|  | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $when | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function onIoFailure($when) | ||||
| 	{ | ||||
| 		$meta = stream_get_meta_data($this->socket); | ||||
| 		if($meta['timed_out']) | ||||
| 			throw new TransportException('Connection timed out '.$when, TransportException::TIMED_OUT); | ||||
| 		throw new TransportException('Connection interrupted '.$when, TransportException::INTERRUPTED); | ||||
| 		$this->assertConnected(); | ||||
| 		$this->flush(); | ||||
| 		$cb = $this->callbacksBuffer; | ||||
| 		$this->callbacksBuffer = []; | ||||
| 		return $cb; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,15 +7,14 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| if(extension_loaded('xmlrpc')) | ||||
| { | ||||
| if (extension_loaded('xmlrpc')) { | ||||
| 	abstract class Request | ||||
| 	{ | ||||
| 		private static $options = array( | ||||
| 		private static $options = [ | ||||
| 			'encoding' => 'utf-8', | ||||
| 			'escaping' => 'markup', | ||||
| 			'verbosity' => 'no_white_space' | ||||
| 		); | ||||
| 		]; | ||||
|  | ||||
| 		/** | ||||
| 		 * @param string $method | ||||
| @@ -25,8 +24,9 @@ if(extension_loaded('xmlrpc')) | ||||
| 		static function encode($method, $args, $escape = true) | ||||
| 		{ | ||||
| 			$opts = self::$options; | ||||
| 			if(!$escape) | ||||
| 				$opts['escaping'] = array(); | ||||
| 			if (!$escape) { | ||||
| 				$opts['escaping'] = []; | ||||
| 			} | ||||
| 			return xmlrpc_encode_request($method, $args, $opts); | ||||
| 		} | ||||
|  | ||||
| @@ -38,21 +38,20 @@ if(extension_loaded('xmlrpc')) | ||||
| 		static function decode($message) | ||||
| 		{ | ||||
| 			$value = xmlrpc_decode_request($message, $method, 'utf-8'); | ||||
| 			if($value === null) | ||||
| 			if ($value === null) { | ||||
| 				throw new ParseException(); | ||||
| 			} | ||||
|  | ||||
| 			if($method === null) | ||||
| 			{ | ||||
| 				if(is_array($value) && xmlrpc_is_fault($value)) | ||||
| 					return array('fault', $value); | ||||
| 				return array('response', $value); | ||||
| 			if ($method === null) { | ||||
| 				if (is_array($value) && xmlrpc_is_fault($value)) { | ||||
| 					return ['fault', $value]; | ||||
| 				} | ||||
| 			return array('call', array($method, $value)); | ||||
| 				return ['response', $value]; | ||||
| 			} | ||||
| 			return ['call', [$method, $value]]; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| else | ||||
| { | ||||
| } else { | ||||
| 	abstract class Request | ||||
| 	{ | ||||
| 		const DATE_FORMAT = 'Ymd\TH:i:s'; | ||||
| @@ -65,12 +64,14 @@ else | ||||
| 		static function encode($method, $args, $escape = true) | ||||
| 		{ | ||||
| 			$xml = '<?xml version="1.0" encoding="utf-8"?><methodCall><methodName>' . self::escape($method, $escape) . '</methodName>'; | ||||
| 			if(!$args) | ||||
| 			if (!$args) { | ||||
| 				return $xml . '<params/></methodCall>'; | ||||
| 			} | ||||
|  | ||||
| 			$xml .= '<params>'; | ||||
| 			foreach($args as $arg) | ||||
| 			foreach ($args as $arg) { | ||||
| 				$xml .= '<param><value>' . self::encodeValue($arg, $escape) . '</value></param>'; | ||||
| 			} | ||||
| 			return $xml . '</params></methodCall>'; | ||||
| 		} | ||||
|  | ||||
| @@ -80,8 +81,7 @@ else | ||||
| 		 */ | ||||
| 		private static function encodeValue($v, $escape = true) | ||||
| 		{ | ||||
| 			switch(gettype($v)) | ||||
| 			{ | ||||
| 			switch (gettype($v)) { | ||||
| 				case 'boolean': | ||||
| 					return '<boolean>' . ((int)$v) . '</boolean>'; | ||||
| 				case 'integer': | ||||
| @@ -89,36 +89,41 @@ else | ||||
| 				case 'double': | ||||
| 					return '<double>' . $v . '</double>'; | ||||
| 				case 'string': | ||||
| 				case 'NULL': | ||||
| 					if(!$v) | ||||
| 					if (strlen($v) === 0) { | ||||
| 						return '<string/>'; | ||||
| 					} | ||||
| 					return '<string>' . self::escape($v, $escape) . '</string>'; | ||||
| 				case 'NULL': | ||||
| 					return '<string/>'; | ||||
| 				case 'object': | ||||
| 					if($v instanceof Base64) | ||||
| 					{ | ||||
| 						if(!$v->scalar) | ||||
| 					if ($v instanceof Base64) { | ||||
| 						if (!$v->scalar) { | ||||
| 							return '<base64/>'; | ||||
| 						} | ||||
| 						return '<base64>' . base64_encode($v->scalar) . '</base64>'; | ||||
| 					} | ||||
| 					if($v instanceof \DateTime) | ||||
| 					if ($v instanceof \DateTime) { | ||||
| 						return '<dateTime.iso8601>' . $v->format(self::DATE_FORMAT) . '</dateTime.iso8601>'; | ||||
| 					} | ||||
| 					$v = get_object_vars($v); | ||||
| 				// fallthrough | ||||
| 				case 'array': | ||||
| 					// empty array case | ||||
| 					if(!$v) | ||||
| 					if (!$v) { | ||||
| 						return '<array><data/></array>'; | ||||
| 					} | ||||
| 					$return = ''; | ||||
| 					// pure array case | ||||
| 					if(array_keys($v) === range(0, count($v) - 1)) | ||||
| 					{ | ||||
| 						foreach($v as $item) | ||||
| 					if (array_keys($v) === range(0, count($v) - 1)) { | ||||
| 						foreach ($v as $item) { | ||||
| 							$return .= '<value>' . self::encodeValue($item, $escape) . '</value>'; | ||||
| 						} | ||||
| 						return '<array><data>' . $return . '</data></array>'; | ||||
| 					} | ||||
| 					// else it's a struct | ||||
| 					foreach($v as $name => $value) | ||||
| 					foreach ($v as $name => $value) { | ||||
| 						$return .= '<member><name>' . self::escape($name, $escape) . '</name><value>' . self::encodeValue($value, $escape) . '</value></member>'; | ||||
| 					} | ||||
| 					return '<struct>' . $return . '</struct>'; | ||||
| 			} | ||||
| 			return ''; | ||||
| @@ -131,8 +136,9 @@ else | ||||
| 		 */ | ||||
| 		private static function escape($str, $escape = true) | ||||
| 		{ | ||||
| 			if($escape) | ||||
| 			if ($escape) { | ||||
| 				return '<![CDATA[' . str_replace(']]>', ']]]]><![CDATA[>', $str) . ']]>'; | ||||
| 			} | ||||
| 			return $str; | ||||
| 		} | ||||
|  | ||||
| @@ -144,19 +150,21 @@ else | ||||
| 		static function decode($message) | ||||
| 		{ | ||||
| 			$xml = @simplexml_load_string($message); | ||||
| 			if(!$xml) | ||||
| 			if (!$xml) { | ||||
| 				throw new ParseException(); | ||||
|  | ||||
| 			if($xml->getName() == 'methodResponse') | ||||
| 			{ | ||||
| 				if($xml->fault) | ||||
| 					return array('fault', self::decodeValue($xml->fault->value)); | ||||
| 				return array('response', self::decodeValue($xml->params->param->value)); | ||||
| 			} | ||||
| 			$params = array(); | ||||
| 			foreach($xml->params->param as $param) | ||||
|  | ||||
| 			if ($xml->getName() == 'methodResponse') { | ||||
| 				if ($xml->fault) { | ||||
| 					return ['fault', self::decodeValue($xml->fault->value)]; | ||||
| 				} | ||||
| 				return ['response', self::decodeValue($xml->params->param->value)]; | ||||
| 			} | ||||
| 			$params = []; | ||||
| 			foreach ($xml->params->param as $param) { | ||||
| 				$params[] = self::decodeValue($param->value); | ||||
| 			return array('call', array((string) $xml->methodName, $params)); | ||||
| 			} | ||||
| 			return ['call', [(string)$xml->methodName, $params]]; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| @@ -167,8 +175,7 @@ else | ||||
| 		{ | ||||
| 			$elt = $elt->children(); | ||||
| 			$elt = $elt[0]; | ||||
| 			switch($elt->getName()) | ||||
| 			{ | ||||
| 			switch ($elt->getName()) { | ||||
| 				case 'boolean': | ||||
| 					return (bool)(int)$elt; | ||||
| 				case 'i4': | ||||
| @@ -183,18 +190,22 @@ else | ||||
| 				case 'dateTime.iso8601': | ||||
| 					return \DateTime::createFromFormat(self::DATE_FORMAT, (string)$elt); | ||||
| 				case 'array': | ||||
| 					$arr = array(); | ||||
| 					foreach($elt->data->value as $v) | ||||
| 					$arr = []; | ||||
| 					foreach ($elt->data->value as $v) { | ||||
| 						$arr[] = self::decodeValue($v); | ||||
| 					} | ||||
| 					return $arr; | ||||
| 				case 'struct': | ||||
| 					$struct = array(); | ||||
| 					foreach($elt as $member) | ||||
| 					$struct = []; | ||||
| 					foreach ($elt as $member) { | ||||
| 						$struct[(string)$member->name] = self::decodeValue($member->value); | ||||
| 					} | ||||
| 					return $struct; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class ParseException extends Exception {} | ||||
| class ParseException extends Exception | ||||
| { | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user