Skip to content

Instantly share code, notes, and snippets.

@Kris-Driv
Last active June 28, 2017 20:18
Show Gist options
  • Save Kris-Driv/a504ee2a4a73590ab7d2424244ed460f to your computer and use it in GitHub Desktop.
Save Kris-Driv/a504ee2a4a73590ab7d2424244ed460f to your computer and use it in GitHub Desktop.
SAMP Query API
<?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