mirror of
https://github.com/xPaw/PHP-Source-Query.git
synced 2026-06-10 23:03:15 +02:00
Docblocks, first pass with Psalm.
This commit is contained in:
+67
-17
@@ -23,29 +23,82 @@ use xPaw\SourceQuery\Exception\SocketException;
|
||||
*
|
||||
* @package xPaw\SourceQuery
|
||||
*
|
||||
* @uses xPaw\SourceQuery\Exception\InvalidPacketException
|
||||
* @uses xPaw\SourceQuery\Exception\SocketException
|
||||
* @uses InvalidPacketException
|
||||
* @uses SocketException
|
||||
*/
|
||||
abstract class BaseSocket
|
||||
{
|
||||
/** @var ?resource */
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
public $Socket;
|
||||
public int $Engine;
|
||||
|
||||
public string $Address;
|
||||
public int $Port;
|
||||
public int $Timeout;
|
||||
/**
|
||||
* @var int $Engine
|
||||
*/
|
||||
public int $Engine = SourceQuery::SOURCE;
|
||||
|
||||
/**
|
||||
* @var string $Address
|
||||
*/
|
||||
public string $Address = '';
|
||||
|
||||
/**
|
||||
* @var int $Port
|
||||
*/
|
||||
public int $Port = 0;
|
||||
|
||||
/**
|
||||
* @var int $Timeout
|
||||
*/
|
||||
public int $Timeout = 0;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->Close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close
|
||||
*/
|
||||
abstract public function Close(): void;
|
||||
|
||||
/**
|
||||
* @param string $Address
|
||||
* @param int $Port
|
||||
* @param int $Timeout
|
||||
* @param int $Engine
|
||||
*/
|
||||
abstract public function Open(string $Address, int $Port, int $Timeout, int $Engine): void;
|
||||
|
||||
/**
|
||||
* @param int $Header
|
||||
* @param string $String
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function Write(int $Header, string $String = ''): bool;
|
||||
|
||||
/**
|
||||
* @param int $Length
|
||||
*
|
||||
* @return Buffer
|
||||
*/
|
||||
abstract public function Read(int $Length = 1400): Buffer;
|
||||
|
||||
/**
|
||||
* @param Buffer $Buffer
|
||||
* @param int $Length
|
||||
* @param callable $SherlockFunction
|
||||
*
|
||||
* @return Buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
* @throws SocketException
|
||||
*/
|
||||
protected function ReadInternal(Buffer $Buffer, int $Length, callable $SherlockFunction): Buffer
|
||||
{
|
||||
if ($Buffer->Remaining() === 0) {
|
||||
@@ -54,12 +107,14 @@ abstract class BaseSocket
|
||||
|
||||
$Header = $Buffer->GetLong();
|
||||
|
||||
if ($Header === -1) { // Single packet
|
||||
// We don't have to do anything
|
||||
} elseif ($Header === -2) { // Split packet
|
||||
// Single packet, do nothing.
|
||||
if ($Header === -1) {
|
||||
return $Buffer;
|
||||
}
|
||||
|
||||
if ($Header === -2) { // Split packet
|
||||
$Packets = [];
|
||||
$IsCompressed = false;
|
||||
$ReadMore = false;
|
||||
$PacketChecksum = null;
|
||||
|
||||
do {
|
||||
@@ -98,18 +153,13 @@ abstract class BaseSocket
|
||||
|
||||
$Packets[ $PacketNumber ] = $Buffer->Get();
|
||||
|
||||
$ReadMore = $PacketCount > sizeof($Packets);
|
||||
$ReadMore = $PacketCount > count($Packets);
|
||||
} while ($ReadMore && $SherlockFunction($Buffer, $Length));
|
||||
|
||||
$Data = implode($Packets);
|
||||
|
||||
// TODO: Test this
|
||||
if ($IsCompressed) {
|
||||
// Let's make sure this function exists, it's not included in PHP by default
|
||||
if (!function_exists('bzdecompress')) {
|
||||
throw new \RuntimeException('Received compressed packet, PHP doesn\'t have Bzip2 library installed, can\'t decompress.');
|
||||
}
|
||||
|
||||
$Data = bzdecompress($Data);
|
||||
|
||||
if (!is_string($Data) || crc32($Data) !== $PacketChecksum) {
|
||||
|
||||
+21
-1
@@ -22,7 +22,7 @@ use xPaw\SourceQuery\Exception\InvalidPacketException;
|
||||
*
|
||||
* @package xPaw\SourceQuery
|
||||
*
|
||||
* @uses xPaw\SourceQuery\Exception\InvalidPacketException
|
||||
* @uses InvalidPacketException
|
||||
*/
|
||||
final class Buffer
|
||||
{
|
||||
@@ -43,6 +43,8 @@ final class Buffer
|
||||
|
||||
/**
|
||||
* Sets buffer
|
||||
*
|
||||
* @param string $Buffer
|
||||
*/
|
||||
public function Set(string $Buffer): void
|
||||
{
|
||||
@@ -61,10 +63,20 @@ final class Buffer
|
||||
return $this->Length - $this->Position;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty(): bool
|
||||
{
|
||||
return $this->Remaining() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets data from buffer
|
||||
*
|
||||
* @param int $Length Bytes to read
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Get(int $Length = -1): string
|
||||
{
|
||||
@@ -97,6 +109,8 @@ final class Buffer
|
||||
|
||||
/**
|
||||
* Get short from buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function GetShort(): int
|
||||
{
|
||||
@@ -111,6 +125,8 @@ final class Buffer
|
||||
|
||||
/**
|
||||
* Get long from buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function GetLong(): int
|
||||
{
|
||||
@@ -125,6 +141,8 @@ final class Buffer
|
||||
|
||||
/**
|
||||
* Get float from buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function GetFloat(): float
|
||||
{
|
||||
@@ -139,6 +157,8 @@ final class Buffer
|
||||
|
||||
/**
|
||||
* Get unsigned long from buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function GetUnsignedLong(): int
|
||||
{
|
||||
|
||||
@@ -15,7 +15,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace xPaw\SourceQuery\Exception;
|
||||
|
||||
abstract class SourceQueryException extends \Exception
|
||||
use Exception;
|
||||
|
||||
abstract class SourceQueryException extends Exception
|
||||
{
|
||||
// Base exception class
|
||||
}
|
||||
|
||||
@@ -33,28 +33,48 @@ final class GoldSourceRcon
|
||||
*
|
||||
* @var BaseSocket
|
||||
*/
|
||||
private $Socket;
|
||||
private BaseSocket $Socket;
|
||||
|
||||
/**
|
||||
* @var string $RconPassword
|
||||
*/
|
||||
private string $RconPassword = '';
|
||||
|
||||
/**
|
||||
* @var string $RconChallenge
|
||||
*/
|
||||
private string $RconChallenge = '';
|
||||
|
||||
/**
|
||||
* @param BaseSocket $Socket
|
||||
*/
|
||||
public function __construct(BaseSocket $Socket)
|
||||
{
|
||||
$this->Socket = $Socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close
|
||||
*/
|
||||
public function Close(): void
|
||||
{
|
||||
$this->RconChallenge = '';
|
||||
$this->RconPassword = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Open
|
||||
*/
|
||||
public function Open(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
public function Write(int $Header, string $String = ''): bool
|
||||
/**
|
||||
* @param string $String
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function Write(string $String = ''): bool
|
||||
{
|
||||
$Command = pack('cccca*', 0xFF, 0xFF, 0xFF, 0xFF, $String);
|
||||
$Length = strlen($Command);
|
||||
@@ -63,17 +83,17 @@ final class GoldSourceRcon
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $Length
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidPacketException
|
||||
*
|
||||
* @return Buffer
|
||||
*/
|
||||
public function Read(int $Length = 1400): Buffer
|
||||
public function Read(): Buffer
|
||||
{
|
||||
// GoldSource RCON has same structure as Query
|
||||
$Buffer = $this->Socket->Read();
|
||||
|
||||
$StringBuffer = '';
|
||||
$ReadMore = false;
|
||||
|
||||
// There is no indentifier of the end, so we just need to continue reading
|
||||
do {
|
||||
@@ -110,23 +130,36 @@ final class GoldSourceRcon
|
||||
return $Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Command
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function Command(string $Command): string
|
||||
{
|
||||
if (!$this->RconChallenge) {
|
||||
throw new AuthenticationException('Tried to execute a RCON command before successful authorization.', AuthenticationException::BAD_PASSWORD);
|
||||
}
|
||||
|
||||
$this->Write(0, 'rcon ' . $this->RconChallenge . ' "' . $this->RconPassword . '" ' . $Command . "\0");
|
||||
$this->Write('rcon ' . $this->RconChallenge . ' "' . $this->RconPassword . '" ' . $Command . "\0");
|
||||
$Buffer = $this->Read();
|
||||
|
||||
return $Buffer->Get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Password
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function Authorize(string $Password): void
|
||||
{
|
||||
$this->RconPassword = $Password;
|
||||
|
||||
$this->Write(0, 'challenge rcon');
|
||||
$this->Write('challenge rcon');
|
||||
$Buffer = $this->Socket->Read();
|
||||
|
||||
if ($Buffer->Get(14) !== 'challenge rcon') {
|
||||
|
||||
+31
-2
@@ -28,15 +28,26 @@ use xPaw\SourceQuery\Exception\SocketException;
|
||||
*/
|
||||
final class Socket extends BaseSocket
|
||||
{
|
||||
/**
|
||||
* Close
|
||||
*/
|
||||
public function Close(): void
|
||||
{
|
||||
if ($this->Socket !== null) {
|
||||
fclose($this->Socket);
|
||||
|
||||
$this->Socket = null;
|
||||
$this->Socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Address
|
||||
* @param int $Port
|
||||
* @param int $Timeout
|
||||
* @param int $Engine
|
||||
*
|
||||
* @throws SocketException
|
||||
*/
|
||||
public function Open(string $Address, int $Port, int $Timeout, int $Engine): void
|
||||
{
|
||||
$this->Timeout = $Timeout;
|
||||
@@ -55,6 +66,12 @@ final class Socket extends BaseSocket
|
||||
stream_set_blocking($this->Socket, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $Header
|
||||
* @param string $String
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function Write(int $Header, string $String = ''): bool
|
||||
{
|
||||
$Command = pack('ccccca*', 0xFF, 0xFF, 0xFF, 0xFF, $Header, $String);
|
||||
@@ -66,9 +83,13 @@ final class Socket extends BaseSocket
|
||||
/**
|
||||
* Reads from socket and returns Buffer.
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
* @param int $Length
|
||||
*
|
||||
* @return Buffer Buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
* @throws SocketException
|
||||
*
|
||||
*/
|
||||
public function Read(int $Length = 1400): Buffer
|
||||
{
|
||||
@@ -80,6 +101,14 @@ final class Socket extends BaseSocket
|
||||
return $Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Buffer $Buffer
|
||||
* @param int $Length
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function Sherlock(Buffer $Buffer, int $Length): bool
|
||||
{
|
||||
$Data = fread($this->Socket, $Length);
|
||||
|
||||
@@ -15,6 +15,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace xPaw\SourceQuery;
|
||||
|
||||
use RuntimeException;
|
||||
use xPaw\SourceQuery\Exception\AuthenticationException;
|
||||
use xPaw\SourceQuery\Exception\InvalidArgumentException;
|
||||
use xPaw\SourceQuery\Exception\InvalidPacketException;
|
||||
@@ -98,11 +99,17 @@ final class SourceQuery
|
||||
*/
|
||||
private bool $UseOldGetChallengeMethod = false;
|
||||
|
||||
/**
|
||||
* @param BaseSocket|null $Socket
|
||||
*/
|
||||
public function __construct(BaseSocket $Socket = null)
|
||||
{
|
||||
$this->Socket = $Socket ?: new Socket();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->Disconnect();
|
||||
@@ -137,7 +144,7 @@ final class SourceQuery
|
||||
*
|
||||
* @param bool $Value Set to true to force old method
|
||||
*
|
||||
* @returns bool Previous value
|
||||
* @return bool Previous value
|
||||
*/
|
||||
public function SetUseOldGetChallengeMethod(bool $Value): bool
|
||||
{
|
||||
@@ -299,7 +306,6 @@ final class SourceQuery
|
||||
if ($Flags & 0x10) {
|
||||
$SteamIDLower = $Buffer->GetUnsignedLong();
|
||||
$SteamIDInstance = $Buffer->GetUnsignedLong(); // This gets shifted by 32 bits, which should be steamid instance
|
||||
$SteamID = 0;
|
||||
|
||||
if (PHP_INT_SIZE === 4) {
|
||||
if (extension_loaded('gmp')) {
|
||||
@@ -307,7 +313,7 @@ final class SourceQuery
|
||||
$SteamIDInstance = gmp_abs($SteamIDInstance);
|
||||
$SteamID = gmp_strval(gmp_or($SteamIDLower, gmp_mul($SteamIDInstance, gmp_pow(2, 32))));
|
||||
} else {
|
||||
throw new \RuntimeException('Either 64-bit PHP installation or "gmp" module is required to correctly parse server\'s steamid.');
|
||||
throw new RuntimeException('Either 64-bit PHP installation or "gmp" module is required to correctly parse server\'s steamid.');
|
||||
}
|
||||
} else {
|
||||
$SteamID = $SteamIDLower | ($SteamIDInstance << 32);
|
||||
@@ -334,7 +340,7 @@ final class SourceQuery
|
||||
$Server[ 'GameID' ] = $Buffer->GetUnsignedLong() | ($Buffer->GetUnsignedLong() << 32);
|
||||
}
|
||||
|
||||
if ($Buffer->Remaining() > 0) {
|
||||
if (!$Buffer->isEmpty()) {
|
||||
throw new InvalidPacketException(
|
||||
'GetInfo: unread data? ' . $Buffer->Remaining() . ' bytes remaining in the buffer. Please report it to the library developer.',
|
||||
InvalidPacketException::BUFFER_NOT_EMPTY
|
||||
@@ -431,7 +437,11 @@ final class SourceQuery
|
||||
/**
|
||||
* Get challenge (used for players/rules packets)
|
||||
*
|
||||
* @param int $Header
|
||||
* @param int $ExpectedResult
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
* @throws SocketException
|
||||
*/
|
||||
private function GetChallenge(int $Header, int $ExpectedResult): void
|
||||
{
|
||||
@@ -488,13 +498,13 @@ final class SourceQuery
|
||||
}
|
||||
|
||||
switch ($this->Socket->Engine) {
|
||||
case SourceQuery::GOLDSOURCE:
|
||||
case self::GOLDSOURCE:
|
||||
{
|
||||
$this->Rcon = new GoldSourceRcon($this->Socket);
|
||||
|
||||
break;
|
||||
}
|
||||
case SourceQuery::SOURCE:
|
||||
case self::SOURCE:
|
||||
{
|
||||
$this->Rcon = new SourceRcon($this->Socket);
|
||||
|
||||
|
||||
@@ -35,15 +35,27 @@ final class SourceRcon
|
||||
*/
|
||||
private BaseSocket $Socket;
|
||||
|
||||
/** @var ?resource */
|
||||
/**
|
||||
* @var ?resource
|
||||
*/
|
||||
private $RconSocket;
|
||||
|
||||
/**
|
||||
* @var int $RconRequestId
|
||||
*/
|
||||
private int $RconRequestId = 0;
|
||||
|
||||
/**
|
||||
* @param BaseSocket $Socket
|
||||
*/
|
||||
public function __construct(BaseSocket $Socket)
|
||||
{
|
||||
$this->Socket = $Socket;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close
|
||||
*/
|
||||
public function Close(): void
|
||||
{
|
||||
if ($this->RconSocket) {
|
||||
@@ -55,6 +67,9 @@ final class SourceRcon
|
||||
$this->RconRequestId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SocketException
|
||||
*/
|
||||
public function Open(): void
|
||||
{
|
||||
if (!$this->RconSocket) {
|
||||
@@ -70,6 +85,12 @@ final class SourceRcon
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $Header
|
||||
* @param string $String
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function Write(int $Header, string $String = ''): bool
|
||||
{
|
||||
// Pack the packet together
|
||||
@@ -82,6 +103,11 @@ final class SourceRcon
|
||||
return $Length === fwrite($this->RconSocket, $Command, $Length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Buffer
|
||||
*
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function Read(): Buffer
|
||||
{
|
||||
$Buffer = new Buffer();
|
||||
@@ -117,6 +143,14 @@ final class SourceRcon
|
||||
return $Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Command
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function Command(string $Command): string
|
||||
{
|
||||
$this->Write(SourceQuery::SERVERDATA_EXECCOMMAND, $Command);
|
||||
@@ -161,6 +195,12 @@ final class SourceRcon
|
||||
return rtrim($Data, "\0");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $Password
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
* @throws InvalidPacketException
|
||||
*/
|
||||
public function Authorize(string $Password): void
|
||||
{
|
||||
$this->Write(SourceQuery::SERVERDATA_AUTH, $Password);
|
||||
|
||||
Reference in New Issue
Block a user