initial commit

Signed-off-by: Brendan Golden <git@brendan.ie>
This commit is contained in:
silver 2023-10-01 02:36:07 +01:00
commit 98aaf7333e
20 changed files with 1067 additions and 0 deletions

55
.gitattributes vendored Normal file
View file

@ -0,0 +1,55 @@
# Borrowed from: https://gitlab.skynet.ie/compsoc/skynet/website/2023/-/blob/main/.gitattributes
# Documents
*.pdf filter=lfs diff=lfs merge=lfs -text
*.doc filter=lfs diff=lfs merge=lfs -text
*.docx filter=lfs diff=lfs merge=lfs -text
# Excel
*.xls filter=lfs diff=lfs merge=lfs -text
*.xlsx filter=lfs diff=lfs merge=lfs -text
*.xlsm filter=lfs diff=lfs merge=lfs -text
# Powerpoints
*.ppt filter=lfs diff=lfs merge=lfs -text
*.pptx filter=lfs diff=lfs merge=lfs -text
*.ppsx filter=lfs diff=lfs merge=lfs -text
# Images
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
# Fonts
*.ttf filter=lfs diff=lfs merge=lfs -text
*.eot filter=lfs diff=lfs merge=lfs -text
*.woff filter=lfs diff=lfs merge=lfs -text
*.woff2 filter=lfs diff=lfs merge=lfs -text
# Video
*.mkv filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.wmv filter=lfs diff=lfs merge=lfs -text
# Misc
*.zip filter=lfs diff=lfs merge=lfs -text
# ET4011
*.cbe filter=lfs diff=lfs merge=lfs -text
*.pbs filter=lfs diff=lfs merge=lfs -text
# Open/Libre office
# from https://www.libreoffice.org/discover/what-is-opendocument/
*.odt filter=lfs diff=lfs merge=lfs -text
*.ods filter=lfs diff=lfs merge=lfs -text
*.odp filter=lfs diff=lfs merge=lfs -text
*.odg filter=lfs diff=lfs merge=lfs -text
# QT
*.ui filter=lfs diff=lfs merge=lfs -text

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
/.idea
boilerplate.html
result
/result

78
css/main.css Normal file
View file

@ -0,0 +1,78 @@
/* CSS is the sound you don't want to hear from a creeper */
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
background: url(../img/bg.jpg) repeat-x #000;
}
#container {
width: 490px;
margin: 30px auto;
}
h3 {
margin: 0;
padding: 0;
color: #eee;
}
p, ol p {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 12px 0 0 0;
padding: 0;
line-height: 150%;
font-size: 14px;
font-style: normal;
color: #aaa;
}
ol {
font: italic 1em Georgia, Times, serif;
color: #999999;
}
ol p {
color: #ccc;
}
listitem {
margin: 12px 0;
}
em {
font-style: normal;
color: #eee;
}
a {
color: #a0cee5;
text-decoration: none;
font-weight: normal;
font-style: normal;
border-bottom: 1px dashed #666;
}
a:hover {
color: #a6d4ea;
}
.hint {
font-size: small;
color: #aaa;
}
.map {
text-transform: uppercase;
font-size: small;
color: #666;
}
#info {
color: #cbcbcb;
border: 1px solid #444;
text-align: left;
text-shadow: #000 0px 1px 3px;
background-color: #2c2c2c;
margin-top: 20px;
padding: 14px;
}

BIN
img/CompSoc_Logo_PNG.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
img/bg.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
img/error.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
img/header.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
img/success.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
img/warning.png (Stored with Git LFS) Normal file

Binary file not shown.

10
index.html Normal file
View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Redirect</title>
<!--<meta http-equiv = "refresh" content = "1; url = http://games.skynet.ie/map/index.html" />-->
</head>
<body>
<p>Redirecting . . .</p>
</body>
</html>

69
index.php Normal file
View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Skynet Games Server</title>
<link rel="stylesheet" type="text/css" href="css/main.css" />
</head>
<body>
<div id="container">
<img src="img/header.jpg"/>
<div id="info">
<h3>Welcome to the UL CompSoc Minecraft server</h3>
<p>To play on the server, you must:
<ol>
<li><p>Own a copy of <a href="http://www.minecraft.net/">Minecraft</a></p></li>
<li><p>Be a member of the UL Computer Society<br/>
<span class="hint">Learn how to join the society <a href="https://ulwolves.ie/society/compsoc">here</a></span></p></li>
<li><p>Get your Minecraft username added to our server's whitelist<br/>
<span class="hint">Email <a href="mailto:minecraft@skynet.ie?subject=Add to MC Whitelist">minecraft@skynet.ie</a></span></p></li>
</ol>
<p>Once that's done, connect to "<em>games.skynet.ie</em>" from the multiplayer section of Minecraft. Enjoy!</p>
<p>You can view a live map of our world at <a href="http://games.skynet.ie/map/index.html">games.skynet.ie/map/</a></p>
<!--<p>Some networks block the minecraft port (25565), but you can get around this by connecting to port 21 instead "<em>games.skynet.ie:21</em>"</p>-->
<p>If you are interested in adding more games to this server contact <a href="mailto:compsoc@skynet.ie">compsoc@skynet.ie</a> and we will see what we can do, we are trialing this server with just Minecraft at the moment</p>
<br/><h3>Minecraft Server Status:</h3>
<?php
$maintence = file_get_contents("maintence.txt");
if($maintence!=1){
require_once('query.php');
$server = new Query('localhost', 25555);
if ($server->connect()){
$info = $server->get_info();
echo "<h4 style='text-align:center;'><img src='./img/success.png' height='20'/> Server is Online <img src='./img/success.png' height='20'/></h4>";
//print_r($info);
$players = $info['players'];
//print_r($players);
if(count($players)>0){
echo "<p>Members Online Now: ";
for($x=0; $x<count($players); $x++){
if($x==(count($players)-1)){
echo "<a href='https://namemc.com/profile/" . $players[$x] . "' target='_blank'>" . $players[$x] . "</a>";
}
else{
echo "<a href='https://namemc.com/profile/" . $players[$x] . "' target='_blank'>" . $players[$x] . "</a>, ";
}
}
echo "</p>";
}
else{
echo "<p>There are currently no members online</p>";
}
}
else{
echo "<h4 style='text-align:center;'><img src='./img/error.png' height='20'/> Server is Offline <img src='./img/error.png' height='20'/></h4>";
}
}
else{
echo "<h4 style='text-align:center;'><img src='./img/warning.png' height='20'/> Server is Undergoing Maintence <img src='./img/warning.png' height='20'/></h4>";
}
?>
</div>
<!--<p class="map">New! <a href="map/">View real-time map</a></p>-->
<p style="text-align:center"><a style="border:none!important" href="http://skynet.ie"><img width="200px" src="img/CompSoc_Logo_PNG.png" /></a></p>
</div>
</body>
</html>

245
lib/MinecraftPing.php Normal file
View file

@ -0,0 +1,245 @@
<?php
namespace xPaw;
class MinecraftPing
{
/*
* Queries Minecraft server
* Returns array on success, false on failure.
*
* WARNING: This method was added in snapshot 13w41a (Minecraft 1.7)
*
* Written by xPaw
*
* Website: http://xpaw.me
* GitHub: https://github.com/xPaw/PHP-Minecraft-Query
*
* ---------
*
* This method can be used to get server-icon.png too.
* Something like this:
*
* $Server = new MinecraftPing( 'localhost' );
* $Info = $Server->Query();
* echo '<img width="64" height="64" src="' . Str_Replace( "\n", "", $Info[ 'favicon' ] ) . '">';
*
*/
private $Socket;
private $ServerAddress;
private $ServerPort;
private $Timeout;
public function __construct( $Address, $Port = 25565, $Timeout = 2, $ResolveSRV = true )
{
$this->ServerAddress = $Address;
$this->ServerPort = (int)$Port;
$this->Timeout = (int)$Timeout;
if( $ResolveSRV )
{
$this->ResolveSRV();
}
$this->Connect( );
}
public function __destruct( )
{
$this->Close( );
}
public function Close( )
{
if( $this->Socket !== null )
{
fclose( $this->Socket );
$this->Socket = null;
}
}
public function Connect( )
{
$connectTimeout = $this->Timeout;
$this->Socket = @fsockopen( $this->ServerAddress, $this->ServerPort, $errno, $errstr, $connectTimeout );
if( !$this->Socket )
{
$this->Socket = null;
throw new MinecraftPingException( "Failed to connect or create a socket: $errno ($errstr)" );
}
// Set Read/Write timeout
stream_set_timeout( $this->Socket, $this->Timeout );
}
public function Query( )
{
$TimeStart = microtime(true); // for read timeout purposes
// See http://wiki.vg/Protocol (Status Ping)
$Data = "\x00"; // packet ID = 0 (varint)
$Data .= "\x04"; // Protocol version (varint)
$Data .= Pack( 'c', StrLen( $this->ServerAddress ) ) . $this->ServerAddress; // Server (varint len + UTF-8 addr)
$Data .= Pack( 'n', $this->ServerPort ); // Server port (unsigned short)
$Data .= "\x01"; // Next state: status (varint)
$Data = Pack( 'c', StrLen( $Data ) ) . $Data; // prepend length of packet ID + data
fwrite( $this->Socket, $Data ); // handshake
fwrite( $this->Socket, "\x01\x00" ); // status ping
$Length = $this->ReadVarInt( ); // full packet length
if( $Length < 10 )
{
return FALSE;
}
fgetc( $this->Socket ); // packet type, in server ping it's 0
$Length = $this->ReadVarInt( ); // string length
$Data = "";
do
{
if (microtime(true) - $TimeStart > $this->Timeout)
{
throw new MinecraftPingException( 'Server read timed out' );
}
$Remainder = $Length - StrLen( $Data );
$block = fread( $this->Socket, $Remainder ); // and finally the json string
// abort if there is no progress
if (!$block)
{
throw new MinecraftPingException( 'Server returned too few data' );
}
$Data .= $block;
} while( StrLen($Data) < $Length );
if( $Data === FALSE )
{
throw new MinecraftPingException( 'Server didn\'t return any data' );
}
$Data = JSON_Decode( $Data, true );
if( JSON_Last_Error( ) !== JSON_ERROR_NONE )
{
if( Function_Exists( 'json_last_error_msg' ) )
{
throw new MinecraftPingException( JSON_Last_Error_Msg( ) );
}
else
{
throw new MinecraftPingException( 'JSON parsing failed' );
}
return FALSE;
}
return $Data;
}
public function QueryOldPre17( )
{
fwrite( $this->Socket, "\xFE\x01" );
$Data = fread( $this->Socket, 512 );
$Len = StrLen( $Data );
if( $Len < 4 || $Data[ 0 ] !== "\xFF" )
{
return FALSE;
}
$Data = SubStr( $Data, 3 ); // Strip packet header (kick message packet and short length)
$Data = iconv( 'UTF-16BE', 'UTF-8', $Data );
// Are we dealing with Minecraft 1.4+ server?
if( $Data[ 1 ] === "\xA7" && $Data[ 2 ] === "\x31" )
{
$Data = Explode( "\x00", $Data );
return Array(
'HostName' => $Data[ 3 ],
'Players' => IntVal( $Data[ 4 ] ),
'MaxPlayers' => IntVal( $Data[ 5 ] ),
'Protocol' => IntVal( $Data[ 1 ] ),
'Version' => $Data[ 2 ]
);
}
$Data = Explode( "\xA7", $Data );
return Array(
'HostName' => SubStr( $Data[ 0 ], 0, -1 ),
'Players' => isset( $Data[ 1 ] ) ? IntVal( $Data[ 1 ] ) : 0,
'MaxPlayers' => isset( $Data[ 2 ] ) ? IntVal( $Data[ 2 ] ) : 0,
'Protocol' => 0,
'Version' => '1.3'
);
}
private function ReadVarInt( )
{
$i = 0;
$j = 0;
while( true )
{
$k = @fgetc( $this->Socket );
if( $k === FALSE )
{
return 0;
}
$k = Ord( $k );
$i |= ( $k & 0x7F ) << $j++ * 7;
if( $j > 5 )
{
throw new MinecraftPingException( 'VarInt too big' );
}
if( ( $k & 0x80 ) != 128 )
{
break;
}
}
return $i;
}
private function ResolveSRV()
{
if( ip2long( $this->ServerAddress ) !== false )
{
return;
}
$Record = dns_get_record( '_minecraft._tcp.' . $this->ServerAddress, DNS_SRV );
if( empty( $Record ) )
{
return;
}
if( isset( $Record[ 0 ][ 'target' ] ) )
{
$this->ServerAddress = $Record[ 0 ][ 'target' ];
}
if( isset( $Record[ 0 ][ 'port' ] ) )
{
$this->ServerPort = $Record[ 0 ][ 'port' ];
}
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace xPaw;
class MinecraftPingException extends \Exception
{
// Exception thrown by MinecraftPing class
}

299
lib/MinecraftQuery.php Normal file
View file

@ -0,0 +1,299 @@
<?php
namespace xPaw;
class MinecraftQuery
{
/*
* Class written by xPaw
*
* Website: http://xpaw.me
* GitHub: https://github.com/xPaw/PHP-Minecraft-Query
*/
const STATISTIC = 0x00;
const HANDSHAKE = 0x09;
private $Socket;
private $Players;
private $Info;
public function Connect( $Ip, $Port = 25565, $Timeout = 3, $ResolveSRV = true )
{
if( !is_int( $Timeout ) || $Timeout < 0 )
{
throw new \InvalidArgumentException( 'Timeout must be an integer.' );
}
if( $ResolveSRV )
{
$this->ResolveSRV( $Ip, $Port );
}
$this->Socket = @FSockOpen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout );
if( $ErrNo || $this->Socket === false )
{
throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
}
Stream_Set_Timeout( $this->Socket, $Timeout );
Stream_Set_Blocking( $this->Socket, true );
try
{
$Challenge = $this->GetChallenge( );
$this->GetStatus( $Challenge );
}
finally
{
FClose( $this->Socket );
}
}
public function ConnectBedrock( $Ip, $Port = 19132, $Timeout = 3, $ResolveSRV = true )
{
if( !is_int( $Timeout ) || $Timeout < 0 )
{
throw new \InvalidArgumentException( 'Timeout must be an integer.' );
}
if( $ResolveSRV )
{
$this->ResolveSRV( $Ip, $Port );
}
$this->Socket = @\fsockopen( 'udp://' . $Ip, (int)$Port, $ErrNo, $ErrStr, $Timeout );
if( $ErrNo || $this->Socket === false )
{
throw new MinecraftQueryException( 'Could not create socket: ' . $ErrStr );
}
\stream_set_timeout( $this->Socket, $Timeout );
\stream_set_blocking( $this->Socket, true );
try
{
$this->GetBedrockStatus();
}
finally
{
FClose( $this->Socket );
}
}
public function GetInfo( )
{
return isset( $this->Info ) ? $this->Info : false;
}
public function GetPlayers( )
{
return isset( $this->Players ) ? $this->Players : false;
}
private function GetChallenge( )
{
$Data = $this->WriteData( self :: HANDSHAKE );
if( $Data === false )
{
throw new MinecraftQueryException( 'Failed to receive challenge.' );
}
return Pack( 'N', $Data );
}
private function GetStatus( $Challenge )
{
$Data = $this->WriteData( self :: STATISTIC, $Challenge . Pack( 'c*', 0x00, 0x00, 0x00, 0x00 ) );
if( !$Data )
{
throw new MinecraftQueryException( 'Failed to receive status.' );
}
$Last = '';
$Info = Array( );
$Data = SubStr( $Data, 11 ); // splitnum + 2 int
$Data = Explode( "\x00\x00\x01player_\x00\x00", $Data );
if( Count( $Data ) !== 2 )
{
throw new MinecraftQueryException( 'Failed to parse server\'s response.' );
}
$Players = SubStr( $Data[ 1 ], 0, -2 );
$Data = Explode( "\x00", $Data[ 0 ] );
// Array with known keys in order to validate the result
// It can happen that server sends custom strings containing bad things (who can know!)
$Keys = Array(
'hostname' => 'HostName',
'gametype' => 'GameType',
'version' => 'Version',
'plugins' => 'Plugins',
'map' => 'Map',
'numplayers' => 'Players',
'maxplayers' => 'MaxPlayers',
'hostport' => 'HostPort',
'hostip' => 'HostIp',
'game_id' => 'GameName'
);
foreach( $Data as $Key => $Value )
{
if( ~$Key & 1 )
{
if( !Array_Key_Exists( $Value, $Keys ) )
{
$Last = false;
continue;
}
$Last = $Keys[ $Value ];
$Info[ $Last ] = '';
}
else if( $Last != false )
{
$Info[ $Last ] = mb_convert_encoding( $Value, 'UTF-8' );
}
}
// Ints
$Info[ 'Players' ] = IntVal( $Info[ 'Players' ] );
$Info[ 'MaxPlayers' ] = IntVal( $Info[ 'MaxPlayers' ] );
$Info[ 'HostPort' ] = IntVal( $Info[ 'HostPort' ] );
// Parse "plugins", if any
if( $Info[ 'Plugins' ] )
{
$Data = Explode( ": ", $Info[ 'Plugins' ], 2 );
$Info[ 'RawPlugins' ] = $Info[ 'Plugins' ];
$Info[ 'Software' ] = $Data[ 0 ];
if( Count( $Data ) == 2 )
{
$Info[ 'Plugins' ] = Explode( "; ", $Data[ 1 ] );
}
}
else
{
$Info[ 'Software' ] = 'Vanilla';
}
$this->Info = $Info;
if( empty( $Players ) )
{
$this->Players = null;
}
else
{
$this->Players = Explode( "\x00", $Players );
}
}
private function GetBedrockStatus( )
{
// 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 );
$Command = \pack( 'cQ', 0x01, time() ); // DefaultMessageIDTypes::ID_UNCONNECTED_PING + 64bit current time
$Command .= $OFFLINE_MESSAGE_DATA_ID;
$Command .= \pack( 'Q', 2 ); // 64bit guid
$Length = \strlen( $Command );
if( $Length !== \fwrite( $this->Socket, $Command, $Length ) )
{
throw new MinecraftQueryException( "Failed to write on socket." );
}
$Data = \fread( $this->Socket, 4096 );
if( $Data === false )
{
throw new MinecraftQueryException( "Failed to read from socket." );
}
if( $Data[ 0 ] !== "\x1C" ) // DefaultMessageIDTypes::ID_UNCONNECTED_PONG
{
throw new MinecraftQueryException( "First byte is not ID_UNCONNECTED_PONG." );
}
if( \substr( $Data, 17, 16 ) !== $OFFLINE_MESSAGE_DATA_ID )
{
throw new MinecraftQueryException( "Magic bytes do not match." );
}
// TODO: What are the 2 bytes after the magic?
$Data = \substr( $Data, 35 );
// TODO: If server-name contains a ';' it is not escaped, and will break this parsing
$Data = \explode( ';', $Data );
$this->Info =
[
'GameName' => $Data[ 0 ],
'HostName' => $Data[ 1 ],
'Unknown1' => $Data[ 2 ], // TODO: What is this?
'Version' => $Data[ 3 ],
'Players' => $Data[ 4 ],
'MaxPlayers' => $Data[ 5 ],
'Unknown2' => $Data[ 6 ], // TODO: What is this?
'Map' => $Data[ 7 ],
'GameMode' => $Data[ 8 ],
'Unknown3' => $Data[ 9 ], // TODO: What is this?
];
$this->Players = null;
}
private function WriteData( $Command, $Append = "" )
{
$Command = Pack( 'c*', 0xFE, 0xFD, $Command, 0x01, 0x02, 0x03, 0x04 ) . $Append;
$Length = StrLen( $Command );
if( $Length !== FWrite( $this->Socket, $Command, $Length ) )
{
throw new MinecraftQueryException( "Failed to write on socket." );
}
$Data = FRead( $this->Socket, 4096 );
if( $Data === false )
{
throw new MinecraftQueryException( "Failed to read from socket." );
}
if( StrLen( $Data ) < 5 || $Data[ 0 ] != $Command[ 2 ] )
{
return false;
}
return SubStr( $Data, 5 );
}
private function ResolveSRV( &$Address, &$Port )
{
if( ip2long( $Address ) !== false )
{
return;
}
$Record = dns_get_record( '_minecraft._tcp.' . $Address, DNS_SRV );
if( empty( $Record ) )
{
return;
}
if( isset( $Record[ 0 ][ 'target' ] ) )
{
$Address = $Record[ 0 ][ 'target' ];
}
}
}

View file

@ -0,0 +1,8 @@
<?php
namespace xPaw;
class MinecraftQueryException extends \Exception
{
// Exception thrown by MinecraftQuery class
}

1
maintence.txt Normal file
View file

@ -0,0 +1 @@
0

211
query.php Normal file
View file

@ -0,0 +1,211 @@
<?php
/**
* Connects to a minecraft server using the [Gamespy?] Query protocol.
* more info on the protocol can be found at
* http://wiki.vg/Query
*
* @copyright 2013 Chris Churchwell
*
*/
class Query {
private $host;
private $port;
private $timeout;
private $token = null;
private $socket;
private $errstr = "";
const SESSION_ID = 2;
const TYPE_HANDSHAKE = 0x09;
const TYPE_STAT = 0x00;
public function __construct($host, $port=25565, $timeout=3, $auto_connect = false) {
$this->host = $host;
$this->port = $port;
$this->timeout = $timeout;
if (is_array($host))
{
$this->host = $host['host'];
$this->port = empty($host['port'])?$port:$host['port'];
$this->timeout = empty($host['timeout'])?$timeout:$host['timeout'];
$auto_connect = empty($host['auto_connect'])?$auto_connect:$host['auto_connect'];
}
if ($auto_connect === true) {
$this->connect();
}
}
/**
* Returns the description of the last error produced.
*
* @return String - Last error string.
*/
public function get_error() {
return $this->errstr;
}
/**
* Checks whether or not the current connection is established.
*
* @return boolean - True if connected; false otherwise.
*/
public function is_connected() {
if (empty($this->token)) return false;
return true;
}
/**
* Disconnects!
* duh
*/
public function disconnect() {
if ($this->socket) {
fclose($this->socket);
}
}
/**
* Connects to the host via UDP with the provided credentials.
* @return boolean - true if successful, false otherwise.
*/
public function connect()
{
$this->socket = fsockopen( 'udp://' . $this->host, $this->port, $errno, $errstr, $this->timeout );
if (!$this->socket)
{
$this->errstr = $errstr;
return false;
}
stream_set_timeout( $this->socket, $this->timeout );
stream_set_blocking( $this->socket, true );
return $this->get_challenge();
}
/**
* Authenticates with the host server and saves the authentication token to a class var.
*
* @return boolean - True if succesfull; false otherwise.
*/
private function get_challenge()
{
if (!$this->socket)
{
return false;
}
//build packet to get challenge.
$packet = pack("c3N", 0xFE, 0xFD, Query::TYPE_HANDSHAKE, Query::SESSION_ID);
//write packet
if ( fwrite($this->socket, $packet, strlen($packet)) === FALSE) {
$this->errstr = "Unable to write to socket";
return false;
}
//read packet.
$response = fread($this->socket, 2056);
if (empty($response)) {
$this->errstr = "Unable to authenticate connection";
return false;
}
$response_data = unpack("c1type/N1id/a*token", $response);
if (!isset($response_data['token']) || empty($response_data['token'])) {
$this->errstr = "Unable to authenticate connection.";
return false;
}
$this->token = $response_data['token'];
return true;
}
/**
* Gets all the info from the server.
*
* @return boolean|array - Returns the data in an array, or false if there was an error.
*/
public function get_info()
{
if (!$this->is_connected()) {
$this->errstr = "Not connected to host";
return false;
}
//build packet to get info
$packet = pack("c3N2", 0xFE, 0xFD, Query::TYPE_STAT, Query::SESSION_ID, $this->token);
//add the full stat thingy.
$packet = $packet . pack("c4", 0x00, 0x00, 0x00, 0x00);
//write packet
if (!fwrite($this->socket, $packet, strlen($packet))) {
$this->errstr = "Unable to write to socket.";
return false;
}
//read packet header
$response = fread($this->socket, 16);
//$response = stream_get_contents($this->socket);
// first byte is type. next 4 are id. dont know what the last 11 are for.
$response_data = unpack("c1type/N1id", $response);
//read the rest of the stream.
$response = fread($this->socket, 2056);
//split the response into 2 parts.
$payload = explode ( "\x00\x01player_\x00\x00" , $response);
$info_raw = explode("\x00", rtrim($payload[0], "\x00"));
//extract key->value chunks from info
$info = array();
foreach (array_chunk($info_raw, 2) as $pair) {
list($key, $value) = $pair;
//strip possible color format codes from hostname
if ($key == "hostname") {
$value = $this->strip_color_codes($value);
}
$info[$key] = $value;
}
//get player data.
$players_raw = rtrim($payload[1], "\x00");
$players = array();
if (!empty($players_raw)) {
$players = explode("\x00", $players_raw);
}
//attach player data to info for simplicity
$info['players'] = $players;
return $info;
}
/**
* Clears Minecraft color codes from a string.
*
* @param String $string - the string to remove the codes from
* @return String - a clean string.
*/
public function strip_color_codes($string) {
return preg_replace('/[\x00-\x1F\x80-\xFF]./', '', $string);
}
}

25
test.php Normal file
View file

@ -0,0 +1,25 @@
<?php
require __DIR__ . '/lib/MinecraftPing.php';
require __DIR__ . '/lib/MinecraftPingException.php';
use xPaw\MinecraftPing;
use xPaw\MinecraftPingException;
try
{
$Query = new MinecraftPing( 'localhost', 25555 );
print_r( $Query->Query() );
}
catch( MinecraftPingException $e )
{
echo $e->getMessage();
}
finally
{
if( $Query )
{
$Query->Close();
}
}
?>

15
test1.php Normal file
View file

@ -0,0 +1,15 @@
<?php
require_once('query.php');
$server = new Query('localhost', 25555);
//or new Query('some.minecraftserver.com', $port, $timeout);
if ($server->connect()){
$info = $server->get_info();
print_r($info);
}
else{
echo "Server Down";
}
?>

21
test2.php Normal file
View file

@ -0,0 +1,21 @@
<?php
require __DIR__ . '/libsrc/MinecraftQuery.php';
require __DIR__ . '/lib/MinecraftQueryException.php';
use xPaw\MinecraftQuery;
use xPaw\MinecraftQueryException;
$Query = new MinecraftQuery( );
try
{
$Query->Connect( 'localhost', 25555 );
print_r( $Query->GetInfo( ) );
print_r( $Query->GetPlayers( ) );
}
catch( MinecraftQueryException $e )
{
echo $e->getMessage( );
}
?>