Skip to content

Commit

Permalink
Add types, minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
xPaw committed Nov 27, 2023
1 parent 68c9938 commit f9470f1
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 59 deletions.
1 change: 1 addition & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ parameters:
level: max
paths:
- src
- .
excludePaths:
- vendor
82 changes: 61 additions & 21 deletions src/MinecraftPing.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,22 @@ class MinecraftPing
*
*/

/** @var ?resource $Socket */
private $Socket;
private $ServerAddress;
private $ServerPort;
private $Timeout;
private string $ServerAddress;
private int $ServerPort;
private float $Timeout;

public function __construct( $Address, $Port = 25565, $Timeout = 2, $ResolveSRV = true )
public function __construct( string $Address, int $Port = 25565, float $Timeout = 2, bool $ResolveSRV = true )
{
if( $Timeout < 0 )
{
throw new \InvalidArgumentException( 'Timeout must be a positive integer.' );
}

$this->ServerAddress = $Address;
$this->ServerPort = (int)$Port;
$this->Timeout = (int)$Timeout;
$this->ServerPort = $Port;
$this->Timeout = $Timeout;

if( $ResolveSRV )
{
Expand All @@ -50,7 +56,7 @@ public function __destruct( )
$this->Close( );
}

public function Close( )
public function Close( ) : void
{
if( $this->Socket !== null )
{
Expand All @@ -60,23 +66,29 @@ public function Close( )
}
}

public function Connect( )
public function Connect( ) : void
{
$this->Socket = @\fsockopen( $this->ServerAddress, $this->ServerPort, $errno, $errstr, (float)$this->Timeout );
$Socket = @\fsockopen( $this->ServerAddress, $this->ServerPort, $errno, $errstr, $this->Timeout );

if( !$this->Socket )
if( $Socket === false )
{
$this->Socket = null;

throw new MinecraftPingException( "Failed to connect or create a socket: $errno ($errstr)" );
}

$this->Socket = $Socket;

// Set Read/Write timeout
\stream_set_timeout( $this->Socket, $this->Timeout );
\stream_set_timeout( $this->Socket, (int)$this->Timeout );
}

public function Query( )
/** @return array|false */
public function Query( ) : array|bool
{
if( $this->Socket === null )
{
throw new MinecraftPingException( 'Socket is not open.' );
}

$TimeStart = \microtime( true ); // for read timeout purposes

// See http://wiki.vg/Protocol (Status Ping)
Expand All @@ -95,7 +107,7 @@ public function Query( )

if( $Length < 10 )
{
return FALSE;
return false;
}

$this->ReadVarInt( ); // packet type, in server ping it's 0
Expand All @@ -104,7 +116,7 @@ public function Query( )

if( $Length < 2 )
{
return FALSE;
return false;
}

$Data = "";
Expand All @@ -116,6 +128,12 @@ public function Query( )
}

$Remainder = $Length - \strlen( $Data );

if( $Remainder <= 0 )
{
break;
}

$block = \fread( $this->Socket, $Remainder ); // and finally the json string
// abort if there is no progress
if( !$block )
Expand All @@ -133,23 +151,45 @@ public function Query( )
throw new MinecraftPingException( 'JSON parsing failed: ' . \json_last_error_msg( ) );
}

if( !\is_array( $Data ) )
{
return false;
}

return $Data;
}

public function QueryOldPre17( )
/** @return array|false */
public function QueryOldPre17( ) : array|bool
{
if( $this->Socket === null )
{
throw new MinecraftPingException( 'Socket is not open.' );
}

\fwrite( $this->Socket, "\xFE\x01" );
$Data = \fread( $this->Socket, 512 );

if( empty( $Data ) )
{
return false;
}

$Len = \strlen( $Data );

if( $Len < 4 || $Data[ 0 ] !== "\xFF" )
{
return FALSE;
return false;
}

$Data = \substr( $Data, 3 ); // Strip packet header (kick message packet and short length)
$Data = \iconv( 'UTF-16BE', 'UTF-8', $Data );

if( $Data === false )
{
return false;
}

// Are we dealing with Minecraft 1.4+ server?
if( $Data[ 1 ] === "\xA7" && $Data[ 2 ] === "\x31" )
{
Expand All @@ -175,7 +215,7 @@ public function QueryOldPre17( )
);
}

private function ReadVarInt( )
private function ReadVarInt( ) : int
{
$i = 0;
$j = 0;
Expand Down Expand Up @@ -207,7 +247,7 @@ private function ReadVarInt( )
return $i;
}

private function ResolveSRV()
private function ResolveSRV() : void
{
if( \ip2long( $this->ServerAddress ) !== false )
{
Expand All @@ -228,7 +268,7 @@ private function ResolveSRV()

if( isset( $Record[ 0 ][ 'port' ] ) )
{
$this->ServerPort = $Record[ 0 ][ 'port' ];
$this->ServerPort = (int)$Record[ 0 ][ 'port' ];
}
}
}
68 changes: 43 additions & 25 deletions src/MinecraftQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,33 @@ class MinecraftQuery
const STATISTIC = 0x00;
const HANDSHAKE = 0x09;

/** @var ?resource $Socket */
private $Socket;
private $Players;
private $Info;
private ?array $Players = null;
private ?array $Info = null;

public function Connect( $Ip, $Port = 25565, $Timeout = 3, $ResolveSRV = true )
public function Connect( string $Ip, int $Port = 25565, float $Timeout = 3, bool $ResolveSRV = true ) : void
{
if( !is_int( $Timeout ) || $Timeout < 0 )
if( $Timeout < 0 )
{
throw new \InvalidArgumentException( 'Timeout must be an integer.' );
throw new \InvalidArgumentException( 'Timeout must be a positive integer.' );
}

if( $ResolveSRV )
{
$this->ResolveSRV( $Ip, $Port );
}

$this->Socket = @\fsockopen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, (float)$Timeout );
$Socket = @\fsockopen( 'udp://' . $Ip, $Port, $ErrNo, $ErrStr, $Timeout );

if( $ErrNo || $this->Socket === false )
if( $ErrNo || $Socket === false )
{
throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
}

\stream_set_timeout( $this->Socket, $Timeout );
$this->Socket = $Socket;

\stream_set_timeout( $this->Socket, (int)$Timeout );
\stream_set_blocking( $this->Socket, true );

try
Expand All @@ -48,13 +51,13 @@ public function Connect( $Ip, $Port = 25565, $Timeout = 3, $ResolveSRV = true )
}
finally
{
\fclose( $this->Socket );
\fclose( $Socket );
}
}

public function ConnectBedrock( $Ip, $Port = 19132, $Timeout = 3, $ResolveSRV = true )
public function ConnectBedrock( string $Ip, int $Port = 19132, float $Timeout = 3, bool $ResolveSRV = true ) : void
{
if( !is_int( $Timeout ) || $Timeout < 0 )
if( $Timeout < 0 )
{
throw new \InvalidArgumentException( 'Timeout must be an integer.' );
}
Expand All @@ -64,14 +67,16 @@ public function ConnectBedrock( $Ip, $Port = 19132, $Timeout = 3, $ResolveSRV =
$this->ResolveSRV( $Ip, $Port );
}

$this->Socket = @\fsockopen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, (float)$Timeout );
$Socket = @\fsockopen( 'udp://' . $Ip, $Port, $ErrNo, $ErrStr, $Timeout );

if( $ErrNo || $this->Socket === false )
if( $ErrNo || $Socket === false )
{
throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
}

\stream_set_timeout( $this->Socket, $Timeout );
$this->Socket = $Socket;

\stream_set_timeout( $this->Socket, (int)$Timeout );
\stream_set_blocking( $this->Socket, true );

try
Expand All @@ -80,23 +85,25 @@ public function ConnectBedrock( $Ip, $Port = 19132, $Timeout = 3, $ResolveSRV =
}
finally
{
\fclose( $this->Socket );
\fclose( $Socket );
}
}

public function GetInfo( )
/** @return array|false */
public function GetInfo( ) : array|bool
{
return isset( $this->Info ) ? $this->Info : false;
}

public function GetPlayers( )
/** @return array|false */
public function GetPlayers( ) : array|bool
{
return isset( $this->Players ) ? $this->Players : false;
}

private function GetChallenge( )
private function GetChallenge( ) : string
{
$Data = $this->WriteData( self :: HANDSHAKE );
$Data = $this->WriteData( self::HANDSHAKE );

if( $Data === false )
{
Expand All @@ -106,9 +113,9 @@ private function GetChallenge( )
return \pack( 'N', $Data );
}

private function GetStatus( $Challenge )
private function GetStatus( string $Challenge ) : void
{
$Data = $this->WriteData( self :: STATISTIC, $Challenge . \pack( 'c*', 0x00, 0x00, 0x00, 0x00 ) );
$Data = $this->WriteData( self::STATISTIC, $Challenge . \pack( 'c*', 0x00, 0x00, 0x00, 0x00 ) );

if( !$Data )
{
Expand Down Expand Up @@ -198,8 +205,13 @@ private function GetStatus( $Challenge )
}
}

private function GetBedrockStatus( )
private function GetBedrockStatus( ) : void
{
if( $this->Socket === null )
{
throw new MinecraftQueryException( 'Socket is not open.' );
}

// hardcoded magic https://github.com/facebookarchive/RakNet/blob/1a169895a900c9fc4841c556e16514182b75faf8/Source/RakPeer.cpp#L135
$OFFLINE_MESSAGE_DATA_ID = \pack( 'c*', 0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78 );

Expand Down Expand Up @@ -255,8 +267,14 @@ private function GetBedrockStatus( )
$this->Players = null;
}

private function WriteData( $Command, $Append = "" )
/** @return string|false */
private function WriteData( int $Command, string $Append = "" ) : string|bool
{
if( $this->Socket === null )
{
throw new MinecraftQueryException( 'Socket is not open.' );
}

$Command = \pack( 'c*', 0xFE, 0xFD, $Command, 0x01, 0x02, 0x03, 0x04 ) . $Append;
$Length = \strlen( $Command );

Expand All @@ -280,7 +298,7 @@ private function WriteData( $Command, $Append = "" )
return \substr( $Data, 5 );
}

private function ResolveSRV( &$Address, &$Port )
private function ResolveSRV( string &$Address, int &$Port ) : void
{
if( \ip2long( $Address ) !== false )
{
Expand All @@ -301,7 +319,7 @@ private function ResolveSRV( &$Address, &$Port )

if( isset( $Record[ 0 ][ 'port' ] ) )
{
$Port = $Record[ 0 ][ 'port' ];
$Port = (int)$Record[ 0 ][ 'port' ];
}
}
}
Loading

0 comments on commit f9470f1

Please sign in to comment.