Last active
June 28, 2017 20:18
-
-
Save Kris-Driv/a504ee2a4a73590ab7d2424244ed460f to your computer and use it in GitHub Desktop.
SAMP Query API
This file contains hidden or 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
<?php | |
// P.S. Kad man nav ko darit, es rakstu datoram instrukcijas. | |
// Čau Signe! | |
/** | |
* SAMP Query Script | |
* | |
* @author Chris-Prime | |
*/ | |
class Buffer { | |
protected $socket; | |
public function __construct($socket, $trimHeader = true) { | |
$this->socket = $socket; | |
if($trimHeader) { | |
$this->read(11); | |
} | |
} | |
public function read($length) { | |
return fread($this->socket, $length); | |
} | |
public function readInt($bytes) { | |
return ord($this->read($bytes)); | |
} | |
} | |
class SampQueryAPI { | |
const INFO = 'i'; | |
const PING = 'p'; | |
const BASIC_PLAYERS = 'c'; | |
const PLAYERS = 'd'; | |
const RULES = 'r'; | |
protected $port, $address; | |
protected $timeout; | |
protected $socket; | |
protected $online = false; | |
protected $randoms = []; | |
public function __construct($address, $port = 7777, $timeout = 2) { | |
$this->address = gethostbyname($address); | |
$this->port = $port; | |
$this->timeout = $timeout; | |
$this->connect(); | |
} | |
public function connect($test = true) { | |
$this->socket = @fsockopen("udp://".$this->address, $this->port, $errno, $errstr, $this->timeout); | |
if(!$this->socket) { | |
throw new \Exception("Couldn't open socket [$errno]: $errstr"); | |
} | |
socket_set_timeout($this->socket, $this->timeout); | |
if($test) { | |
$packet = 'SAMP'; | |
$packet .= chr(strtok($this->address, '.')); | |
$packet .= chr(strtok('.')); | |
$packet .= chr(strtok('.')); | |
$packet .= chr(strtok('.')); | |
$packet .= chr($this->port & 0xFF); | |
$packet .= chr($this->port >> 8 & 0xFF); | |
$packet .= 'p4150'; | |
fwrite($this->socket, $packet); | |
if(fread($this->socket, 10)) | |
{ | |
if(fread($this->socket, 5) == 'p4150') | |
{ | |
$this->online = true; | |
return; | |
} | |
} | |
$this->online = false; | |
} | |
} | |
public function isOnline() { | |
return $this->online; | |
} | |
public function getInfo() { | |
$this->sendPacket($this->buildPacket(self::INFO)); | |
$buf = new Buffer($this->socket, true); | |
$d = array(); | |
$d["Password"] = $buf->readInt(1); | |
$d["Players"] = $buf->readInt(2); | |
$d["MaxPlayers"] = $buf->readInt(2); | |
$d["Hostname"] = $buf->read($buf->readInt(4)); | |
$d["Gamemode"] = $buf->read($buf->readInt(4)); | |
$d["Mapname"] = $buf->read($buf->readInt(4)); | |
return $d; | |
} | |
public function getPing() { | |
$n = microtime(true); | |
$this->sendPacket($this->buildPacket(self::PING)); | |
$buf = new Buffer($this->socket, true); | |
$d = ["Ping" => microtime(true) - $n]; | |
foreach ($this->randoms as $k => $r) { | |
$rr = $buf->readInt(1); | |
if($rr !== $r) { | |
throw new \Exception("Ping number mismatch ($k: $r !== $rr)!"); | |
} | |
} | |
return $d; | |
} | |
public function getRules() { | |
$this->sendPacket($this->buildPacket(self::RULES)); | |
$d = []; | |
$buf = new Buffer($this->socket, true); | |
$d["Rules"] = []; | |
$rc = $buf->readInt(2); | |
for($i = 0; $i < $rc; $i++) { | |
$d["Rules"][$i]["Rulename"] = $buf->read($buf->readInt(1)); | |
$d["Rules"][$i]["Value"] = $buf->read($buf->readInt(1)); | |
} | |
return $d; | |
} | |
public function getBasicPlayers() { | |
$this->sendPacket($this->buildPacket(self::BASIC_PLAYERS)); | |
$buf = new Buffer($this->socket); | |
$d = []; | |
$iPlayerCount = $buf->readInt(2); | |
if($iPlayerCount > 0) | |
{ | |
for($iIndex = 0; $iIndex < $iPlayerCount; ++$iIndex) | |
{ | |
$iStrlen = $buf->readInt(1); | |
$d[] = array | |
( | |
"nickname" => $buf->read($iStrlen), | |
"score" => $buf->readInt(4), | |
); | |
} | |
} | |
return $d; | |
} | |
public function getPlayers() { | |
$this->sendPacket($this->buildPacket(self::PLAYERS)); | |
$buf = new Buffer($this->socket); | |
$d = []; | |
$iPlayerCount = $buf->readInt(2); | |
for ($i=0; $i < $iPlayerCount; $i++) { | |
$d["Players"][$i]["PlayerID"] = $buf->readInt(1); | |
$d["Players"][$i]["PlayerNick"] = $buf->read($buf->readInt(1)); | |
$d["Players"][$i]["Score"] = $buf->readInt(4); | |
$d["Players"][$i]["Ping"] = $buf->readInt(4); | |
} | |
return $d; | |
} | |
public function buildPacket($opcode) { | |
$bin = "SAMP"; | |
foreach (explode(".", $this->address) as $byte) { | |
$bin .= chr($byte); | |
} | |
$bin .= chr($this->port & 0xFF); | |
$bin .= chr($this->port >> 8 & 0xFF); | |
$bin .= $opcode; | |
if($opcode === self::PING) { // If sending a ping packet | |
for($i = 0; $i < 4; $i++) { | |
$bin .= chr($this->randoms[$i] = mt_rand(0, 255)); | |
} | |
} | |
return $bin; | |
} | |
public function sendPacket($packet) { | |
if(!$this->isOnline()) throw new Exception("Can't send packet to offline server"); | |
fwrite($this->socket, $packet); | |
} | |
public function disconnect() { | |
@fclose($this->socket); | |
} | |
} | |
$s = new SampQueryAPI("rp.samp.lv", 7777, 5); | |
echo "<pre>"; | |
if($s->isOnline()) { | |
var_dump($s->getInfo()); | |
var_dump($s->getPing()); | |
var_dump($s->getPlayers()); | |
var_dump($s->getBasicPlayers()); | |
var_dump($s->getRules()); | |
} else { | |
die("Can't connect"); | |
} | |
$s->disconnect(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment