gudan garam
Advanced OT User
Sometime ago I was roaming on otland and found this topic.
It is gesior's version of a players list reader that reads from the status protocol of TFS.
His version could only get the players list, so I finished it and it is now possible to read m more info the status protocol gives about the server (I have not implemented REQUEST_PLAYER_STATUS_INFO which is for looking if a certain player is online). (credits for the base code for Gesior, ofc).
You can change the
This is a sample output:
This script is more like a prof of conecpt, it helped me understand this protocol better and I hope it helps you aswell. I haven't been able to parse the software info response, I also haven't looked deep into it, it can probably something simple to fix idk.
Code:
If you want to create your own otservlist, you can use this to base your system.
It is gesior's version of a players list reader that reads from the status protocol of TFS.
His version could only get the players list, so I finished it and it is now possible to read m more info the status protocol gives about the server (I have not implemented REQUEST_PLAYER_STATUS_INFO which is for looking if a certain player is online). (credits for the base code for Gesior, ofc).
You can change the
$packets
array to only get the data you want.This is a sample output:
This script is more like a prof of conecpt, it helped me understand this protocol better and I hope it helps you aswell. I haven't been able to parse the software info response, I also haven't looked deep into it, it can probably something simple to fix idk.
Code:
PHP:
<?php
class OTStatusParser
{
public $players = array();
public $playersCount = 0;
public $statusBinaryData = '';
public $basicServerInfo = '';
public $ownerInfo = '';
public $miscInfo = '';
public $mapInfo = '';
public $softwareInfo = '';
public $playersInfo = '';
/*
basic binary decoder from ItemAttributes of gesior acc. maker 2012
START
*/
public function __construct($statusBinaryData)
{
$this->statusBinaryData = $statusBinaryData;
$this->parseData();
}
private function getByte()
{
$ret = ord($this->statusBinaryData[0]);
$this->statusBinaryData = substr($this->statusBinaryData, 1);
return $ret;
}
private function getU16()
{
$ret = ord($this->statusBinaryData[0]) + ord($this->statusBinaryData[1]) * 256;
$this->statusBinaryData = substr($this->statusBinaryData, 2);
return $ret;
}
private function getU32()
{
$ret = ord($this->statusBinaryData[0]) + ord($this->statusBinaryData[1]) * 256 + ord($this->statusBinaryData[2]) * 65536 + ord($this->statusBinaryData[3]) * 16777216;
$this->statusBinaryData = substr($this->statusBinaryData, 4);
return $ret;
}
private function getString()
{
$length = $this->getU16();
$ret = substr($this->statusBinaryData, 0, $length);
$this->statusBinaryData = substr($this->statusBinaryData, $length);
return $ret;
}
public function parseData()
{
if(!empty($this->statusBinaryData))
{
while (!empty($this->statusBinaryData)){
$firstByte = $this->getByte(); // first byte of players list is 0x21
switch ($firstByte) {
case 33: // player list
$this->playersCount = $this->getU32(); // then players count in U32
$i = 0;
while($i < $this->playersCount) // now read all players
{
$playerName = $this->getString(); // get string, string 'length' is in U16 before chain of letters [tfs format]
$level = $this->getU32(); // get level of player, its in U32
// add player to list
$this->players[] = array('name' => $playerName, 'level' => $level);
$i++;
}
break;
case 16:
$this->basicServerInfo .= "[Name] \t\t {$this->getString()}" . PHP_EOL;
$this->basicServerInfo .= "[IP] \t\t {$this->getString()}" . PHP_EOL;
$this->basicServerInfo .= "[Login Port] \t {$this->getString()}" . PHP_EOL;
break;
case 17:
$this->ownerInfo .= "[Owner Name] \t {$this->getString()}" . PHP_EOL;
$this->ownerInfo .= "[Owner Email] \t {$this->getString()}" . PHP_EOL;
break;
case 18:
$this->miscInfo .= "[MOTD] \t \t {$this->getString()}" . PHP_EOL;
$this->miscInfo .= "[Location] \t {$this->getString()}" . PHP_EOL;
$this->miscInfo .= "[URL] \t \t {$this->getString()}" . PHP_EOL;
$this->getU32();
$this->getU32();
break;
case 32:
$this->playersInfo .= "[Online Now] \t {$this->getU32()}" . PHP_EOL;
$this->playersInfo .= "[Max Players] \t {$this->getU32()}" . PHP_EOL;
$this->playersInfo .= "[Record] \t {$this->getU32()}" . PHP_EOL;
break;
case 48:
$this->mapInfo .= "[Name] \t\t {$this->getString()}" . PHP_EOL;
$this->mapInfo .= "[Author] \t {$this->getString()}" . PHP_EOL;
$this->mapInfo .= "[Size] \t\t {$this->getU16()}x{$this->getU16()}" . PHP_EOL;
break;
case 43:
$this->softwareInfo .= "[Name] \t\t {$this->getString()}" . PHP_EOL;
$this->softwareInfo .= "[Version] \t {$this->getString()}" . PHP_EOL;
$this->softwareInfo .= "[Version Str] \t {$this->getString()}" . PHP_EOL;
break;
}
}
}
}
public function getPlayers()
{
return $this->players;
}
public static function removeLength($data) {
return substr($data, 2);
}
}
$packets = [
// 'Players list info' => chr(6).chr(0).chr(255).chr(1).chr(32).chr(0).chr(0).chr(0), uncomment this line if you want a online playrs list (big output)
'basic server info' => chr(6).chr(0).chr(255).chr(1).chr(1).chr(0).chr(0).chr(0),
'server owner info' => chr(6).chr(0).chr(255).chr(1).chr(2).chr(0).chr(0).chr(0),
'misc server info' => chr(6).chr(0).chr(255).chr(1).chr(4).chr(0).chr(0).chr(0),
'players info' => chr(6).chr(0).chr(255).chr(1).chr(8).chr(0).chr(0).chr(0),
'server map info' => chr(6).chr(0).chr(255).chr(1).chr(16).chr(0).chr(0).chr(0),
];
/*
---------
S T A R T
O F
C O D E
---------
*/
try {
$ip = 'new.cronara.com';
$port = 7171;
$resp = '';
foreach ($packets as $p => $toSend) {
$sock = @fsockopen($ip, $port, $errno, $errstr, 1);
if ($sock) {
echo "[". date("Y/m/d h:i:sa") ."] Getting $p." . PHP_EOL;
fwrite($sock, $toSend);
$answer = '';
while (!feof($sock)) // read text from server as long as there is anything to read
{
$answer .= fgets($sock, 1024);
}
$answer = OTStatusParser::removeLength($answer);
$resp .= $answer;
fclose($sock);
if ($p < count($packets) - 1) {
sleep(6); // We need to wait 5 seconds or else the server will disconnect us.
}
}
}
echo "[". date("Y/m/d h:i:sa") ."] Parsing the information received." . PHP_EOL . PHP_EOL;
$spl = new OTStatusParser($resp);
foreach($spl->getPlayers() as $player)
{
echo 'Name: ' . htmlspecialchars($player['name']) . ', level: ' . htmlspecialchars($player['level']) . PHP_EOL;
}
//if (!empty($spl->basicServerInfo)) {
echo "> Basic Server Info: " . PHP_EOL;
echo $spl->basicServerInfo;
echo PHP_EOL;
//}
// if (!empty($spl->basicServerInfo)) {
echo "> Misc Info: " . PHP_EOL;
echo $spl->miscInfo;
echo PHP_EOL;
// }
// if (!empty($spl->basicServerInfo)) {
echo "> Players Info: " . PHP_EOL;
echo $spl->playersInfo;
echo PHP_EOL;
// }
// if (!empty($spl->basicServerInfo)) {
echo "> Owner Info: " . PHP_EOL;
echo $spl->ownerInfo;
echo PHP_EOL;
// }
// if (!empty($spl->basicServerInfo)) {
echo "> Map Info: " . PHP_EOL;
echo $spl->mapInfo;
echo PHP_EOL;
// }
// if (!empty($spl->basicServerInfo)) {
echo "> Software Info: " . PHP_EOL;
echo $spl->softwareInfo;
echo PHP_EOL;
// }
} catch (\Exception $e) {
var_dump($e->getMessage());
}
If you want to create your own otservlist, you can use this to base your system.