-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c9e48af
commit c2e542f
Showing
18 changed files
with
1,636 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
<?php | ||
require __DIR__ . '/SourceQuery/bootstrap.php'; | ||
|
||
use xPaw\SourceQuery\SourceQuery; | ||
|
||
// For the sake of this example | ||
Header( 'Content-Type: text/plain' ); | ||
Header( 'X-Content-Type-Options: nosniff' ); | ||
|
||
// Edit this -> | ||
define( 'SQ_SERVER_ADDR', '216.52.148.47' ); | ||
define( 'SQ_SERVER_PORT', 27015 ); | ||
define( 'SQ_TIMEOUT', 1 ); | ||
define( 'SQ_ENGINE', SourceQuery::SOURCE ); | ||
// Edit this <- | ||
|
||
$Query = new SourceQuery( ); | ||
|
||
try | ||
{ | ||
$Query->Connect( SQ_SERVER_ADDR, SQ_SERVER_PORT, SQ_TIMEOUT, SQ_ENGINE ); | ||
|
||
print_r( $Query->GetInfo( ) ); | ||
print_r( $Query->GetPlayers( ) ); | ||
print_r( $Query->GetRules( ) ); | ||
} | ||
catch( Exception $e ) | ||
{ | ||
echo $e->getMessage( ); | ||
} | ||
finally | ||
{ | ||
$Query->Disconnect( ); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<?php | ||
/** | ||
* @author Pavel Djundik <[email protected]> | ||
* | ||
* @link https://xpaw.me | ||
* @link https://github.com/xPaw/PHP-Source-Query | ||
* | ||
* @license GNU Lesser General Public License, version 2.1 | ||
* | ||
* @internal | ||
*/ | ||
|
||
namespace xPaw\SourceQuery; | ||
|
||
use xPaw\SourceQuery\Exception\InvalidPacketException; | ||
|
||
/** | ||
* Base socket interface | ||
* | ||
* @package xPaw\SourceQuery | ||
* | ||
* @uses xPaw\SourceQuery\Exception\InvalidPacketException | ||
*/ | ||
abstract class BaseSocket | ||
{ | ||
public $Socket; | ||
public $Engine; | ||
|
||
public $Address; | ||
public $Port; | ||
public $Timeout; | ||
|
||
public function __destruct( ) | ||
{ | ||
$this->Close( ); | ||
} | ||
|
||
abstract public function Close( ); | ||
abstract public function Open( $Address, $Port, $Timeout, $Engine ); | ||
abstract public function Write( $Header, $String = '' ); | ||
abstract public function Read( $Length = 1400 ); | ||
|
||
protected function ReadInternal( $Buffer, $Length, $SherlockFunction ) | ||
{ | ||
if( $Buffer->Remaining( ) === 0 ) | ||
{ | ||
throw new InvalidPacketException( 'Failed to read any data from socket', InvalidPacketException::BUFFER_EMPTY ); | ||
} | ||
|
||
$Header = $Buffer->GetLong( ); | ||
|
||
if( $Header === -1 ) // Single packet | ||
{ | ||
// We don't have to do anything | ||
} | ||
else if( $Header === -2 ) // Split packet | ||
{ | ||
$Packets = []; | ||
$IsCompressed = false; | ||
$ReadMore = false; | ||
|
||
do | ||
{ | ||
$RequestID = $Buffer->GetLong( ); | ||
|
||
switch( $this->Engine ) | ||
{ | ||
case SourceQuery::GOLDSOURCE: | ||
{ | ||
$PacketCountAndNumber = $Buffer->GetByte( ); | ||
$PacketCount = $PacketCountAndNumber & 0xF; | ||
$PacketNumber = $PacketCountAndNumber >> 4; | ||
|
||
break; | ||
} | ||
case SourceQuery::SOURCE: | ||
{ | ||
$IsCompressed = ( $RequestID & 0x80000000 ) !== 0; | ||
$PacketCount = $Buffer->GetByte( ); | ||
$PacketNumber = $Buffer->GetByte( ) + 1; | ||
|
||
if( $IsCompressed ) | ||
{ | ||
$Buffer->GetLong( ); // Split size | ||
|
||
$PacketChecksum = $Buffer->GetUnsignedLong( ); | ||
} | ||
else | ||
{ | ||
$Buffer->GetShort( ); // Split size | ||
} | ||
|
||
break; | ||
} | ||
} | ||
|
||
$Packets[ $PacketNumber ] = $Buffer->Get( ); | ||
|
||
$ReadMore = $PacketCount > sizeof( $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( CRC32( $Data ) !== $PacketChecksum ) | ||
{ | ||
throw new InvalidPacketException( 'CRC32 checksum mismatch of uncompressed packet data.', InvalidPacketException::CHECKSUM_MISMATCH ); | ||
} | ||
} | ||
|
||
$Buffer->Set( SubStr( $Data, 4 ) ); | ||
} | ||
else | ||
{ | ||
throw new InvalidPacketException( 'Socket read: Raw packet header mismatch. (0x' . DecHex( $Header ) . ')', InvalidPacketException::PACKET_HEADER_MISMATCH ); | ||
} | ||
|
||
return $Buffer; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
<?php | ||
/** | ||
* @author Pavel Djundik <[email protected]> | ||
* | ||
* @link https://xpaw.me | ||
* @link https://github.com/xPaw/PHP-Source-Query | ||
* | ||
* @license GNU Lesser General Public License, version 2.1 | ||
* | ||
* @internal | ||
*/ | ||
|
||
namespace xPaw\SourceQuery; | ||
|
||
use xPaw\SourceQuery\Exception\InvalidPacketException; | ||
|
||
/** | ||
* Class Buffer | ||
* | ||
* @package xPaw\SourceQuery | ||
* | ||
* @uses xPaw\SourceQuery\Exception\InvalidPacketException | ||
*/ | ||
class Buffer | ||
{ | ||
/** | ||
* Buffer | ||
* | ||
* @var string | ||
*/ | ||
private $Buffer; | ||
|
||
/** | ||
* Buffer length | ||
* | ||
* @var int | ||
*/ | ||
private $Length; | ||
|
||
/** | ||
* Current position in buffer | ||
* | ||
* @var int | ||
*/ | ||
private $Position; | ||
|
||
/** | ||
* Sets buffer | ||
* | ||
* @param string $Buffer Buffer | ||
*/ | ||
public function Set( $Buffer ) | ||
{ | ||
$this->Buffer = $Buffer; | ||
$this->Length = StrLen( $Buffer ); | ||
$this->Position = 0; | ||
} | ||
|
||
/** | ||
* Get remaining bytes | ||
* | ||
* @return int Remaining bytes in buffer | ||
*/ | ||
public function Remaining( ) | ||
{ | ||
return $this->Length - $this->Position; | ||
} | ||
|
||
/** | ||
* Gets data from buffer | ||
* | ||
* @param int $Length Bytes to read | ||
* | ||
* @return string | ||
*/ | ||
public function Get( $Length = -1 ) | ||
{ | ||
if( $Length === 0 ) | ||
{ | ||
return ''; | ||
} | ||
|
||
$Remaining = $this->Remaining( ); | ||
|
||
if( $Length === -1 ) | ||
{ | ||
$Length = $Remaining; | ||
} | ||
else if( $Length > $Remaining ) | ||
{ | ||
return ''; | ||
} | ||
|
||
$Data = SubStr( $this->Buffer, $this->Position, $Length ); | ||
|
||
$this->Position += $Length; | ||
|
||
return $Data; | ||
} | ||
|
||
/** | ||
* Get byte from buffer | ||
* | ||
* @return int | ||
*/ | ||
public function GetByte( ) | ||
{ | ||
return Ord( $this->Get( 1 ) ); | ||
} | ||
|
||
/** | ||
* Get short from buffer | ||
* | ||
* @return int | ||
*/ | ||
public function GetShort( ) | ||
{ | ||
if( $this->Remaining( ) < 2 ) | ||
{ | ||
throw new InvalidPacketException( 'Not enough data to unpack a short.', InvalidPacketException::BUFFER_EMPTY ); | ||
} | ||
|
||
$Data = UnPack( 'v', $this->Get( 2 ) ); | ||
|
||
return $Data[ 1 ]; | ||
} | ||
|
||
/** | ||
* Get long from buffer | ||
* | ||
* @return int | ||
*/ | ||
public function GetLong( ) | ||
{ | ||
if( $this->Remaining( ) < 4 ) | ||
{ | ||
throw new InvalidPacketException( 'Not enough data to unpack a long.', InvalidPacketException::BUFFER_EMPTY ); | ||
} | ||
|
||
$Data = UnPack( 'l', $this->Get( 4 ) ); | ||
|
||
return $Data[ 1 ]; | ||
} | ||
|
||
/** | ||
* Get float from buffer | ||
* | ||
* @return float | ||
*/ | ||
public function GetFloat( ) | ||
{ | ||
if( $this->Remaining( ) < 4 ) | ||
{ | ||
throw new InvalidPacketException( 'Not enough data to unpack a float.', InvalidPacketException::BUFFER_EMPTY ); | ||
} | ||
|
||
$Data = UnPack( 'f', $this->Get( 4 ) ); | ||
|
||
return $Data[ 1 ]; | ||
} | ||
|
||
/** | ||
* Get unsigned long from buffer | ||
* | ||
* @return int | ||
*/ | ||
public function GetUnsignedLong( ) | ||
{ | ||
if( $this->Remaining( ) < 4 ) | ||
{ | ||
throw new InvalidPacketException( 'Not enough data to unpack an usigned long.', InvalidPacketException::BUFFER_EMPTY ); | ||
} | ||
|
||
$Data = UnPack( 'V', $this->Get( 4 ) ); | ||
|
||
return $Data[ 1 ]; | ||
} | ||
|
||
/** | ||
* Read one string from buffer ending with null byte | ||
* | ||
* @return string | ||
*/ | ||
public function GetString( ) | ||
{ | ||
$ZeroBytePosition = StrPos( $this->Buffer, "\0", $this->Position ); | ||
|
||
if( $ZeroBytePosition === false ) | ||
{ | ||
return ''; | ||
} | ||
|
||
$String = $this->Get( $ZeroBytePosition - $this->Position ); | ||
|
||
$this->Position++; | ||
|
||
return $String; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?php | ||
/** | ||
* @author Pavel Djundik <[email protected]> | ||
* | ||
* @link https://xpaw.me | ||
* @link https://github.com/xPaw/PHP-Source-Query | ||
* | ||
* @license GNU Lesser General Public License, version 2.1 | ||
* | ||
* @internal | ||
*/ | ||
|
||
namespace xPaw\SourceQuery\Exception; | ||
|
||
class AuthenticationException extends SourceQueryException | ||
{ | ||
const BAD_PASSWORD = 1; | ||
const BANNED = 2; | ||
} |
Oops, something went wrong.