• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

OTS Advanced Status [players list] reader

Gesior.pl

Mega Noob&LOL 2012
Senator
Joined
Sep 18, 2007
Messages
2,968
Solutions
99
Reaction score
3,384
Location
Poland
GitHub
gesior
Most of you don't know (don't care), but there is something called 'advanced status' (very usefull to detect servers that spoof info about number if players). You can get from port 7171 of any server status in XML format (with number of players online, record, server name etc.), but you can get also list of online players names and their levels. Example how to get it is in this script:
PHP:
<?php
class ServerPlayersList
{
    public $players = array();
    public $playersCount = 0;
    public $playersBinaryData = '';
/*
basic binary decoder from ItemAttributes of gesior acc. maker 2012
START
*/
    public function __construct($playersBinaryData)
    {
        $this->loadPlayers($playersBinaryData);
    }

    private function getByte()
    {
        $ret = ord($this->playersBinaryData[0]);
        $this->playersBinaryData = substr($this->playersBinaryData, 1);
        return $ret;
    }

    private function getU16()
    {
        $ret = ord($this->playersBinaryData[0]) + ord($this->playersBinaryData[1]) * 256;
        $this->playersBinaryData = substr($this->playersBinaryData, 2);
        return $ret;
    }

    private function getU32()
    {
        $ret = ord($this->playersBinaryData[0]) + ord($this->playersBinaryData[1]) * 256 + ord($this->playersBinaryData[2]) * 65536 + ord($this->playersBinaryData[3]) * 16777216;
        $this->playersBinaryData = substr($this->playersBinaryData, 4);
        return $ret;
    }

    private function getString($length)
    {
        $ret = substr($this->playersBinaryData, 0, $length);
        $this->playersBinaryData = substr($this->playersBinaryData, $length);
        return $ret;
    }
/*
basic binary decoder from ItemAttributes of gesior acc. maker 2012
END
*/
    public function loadPlayers($playersBinaryData)
    {
        /*
        short info:
        byte (U8) is 1 byte length number
        short (U16) is 2 byte length number
        int (U32) is 4 byte length number
        string is chain of letters [bytes] (OTS always send U16 before that chain to let us know how many letters we must read)
        */
        $this->playersBinaryData = $playersBinaryData;
        if(!empty($this->playersBinaryData))
        {
            $packetLength = $this->getU16(); // useless for us
            $firstByte = $this->getByte(); // first byte of players list is 0x21
            if($firstByte == 33) // if first byte is 0x21 (2 * 16 + 1 = 33)
            {
                $this->playersCount = $this->getU32(); // then players count in U32
                while(!empty($this->playersBinaryData)) // now read all players
                {
                    $playerName = $this->getString($this->getU16()); // 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);
                }
            }
        }
    }

    public function getPlayers()
    {
        return $this->players;
    }
}
// OUR PACKET:
$toSend = chr(6).chr(0).chr(255).chr(1).chr(32).chr(0).chr(0).chr(0);
// 6,0,255 = we want status
// 1 = we want some special 'binary' status, not 'XML' basic info (XML info = 255 [0xFF])
// 32 (0x20) = we want players list
// 0,0,0 = idk why must be x3, not x1 [server reads U32 in place of U16?]
/*
available binary status list (more info in source file 'status.cpp'):
    REQUEST_BASIC_SERVER_INFO     = 0x01 // 1
    REQUEST_SERVER_OWNER_INFO    = 0x02 // 2
    REQUEST_MISC_SERVER_INFO    = 0x04 // 4
    REQUEST_PLAYERS_INFO        = 0x08 // 8
    REQUEST_SERVER_MAP_INFO        = 0x10 // 16
    REQUEST_EXT_PLAYERS_INFO    = 0x20 // 32
    REQUEST_PLAYER_STATUS_INFO    = 0x40 // 64
    REQUEST_SERVER_SOFTWARE_INFO    = 0x80 // 128
*/

/*
---------
S T A R T
O F
C O D E
---------
*/

$ip = 'gamescraft.net';
$port = 7171;
$sock = @fsockopen($ip, $port, $errno, $errstr, 1);
if($sock)
{
    fwrite($sock, $toSend); // send our 8 bytes that tells server: 'please send me players list'
    $answer = ''; 
    while (!feof($sock)) // read text from server as long as there is anything to read
    {
        $answer .= fgets($sock, 1024);
    }
    fclose($sock);
    // this script demonstrates how to parse REQUEST_EXT_PLAYERS_INFO [list of players online and their levels] ONLY!
    $spl = new ServerPlayersList($answer);
    foreach($spl->getPlayers() as $player)
    {
        echo 'Name: ' . htmlspecialchars($player['name']) . ', level: ' . htmlspecialchars($player['level']) . '<br />';
    }
}
else
    echo 'Server blocked script. Probably you must wait 5 minutes [config.lua: statusTimeout = 5 * 60 * 1000]';
Server generates that information in status.cpp and sends in 'binary' (0010110..) format. You don't have to understand binary format. You can use my class ServerPlayersList to get players names and levels :)

Example result:
Name: Kshadowe, level: 90
Name: Geeme, level: 86
Name: Master Kellir, level: 80
Name: Home Simpson, level: 97
Name: Tomurka, level: 117
Name: Rei Leao, level: 84
Name: Paola Druidinha, level: 112
Name: Zeck Lester, level: 118
Name: Cadeirante Vitor, level: 117
Name: Bitche, level: 134
Name: Leaao Niice, level: 142
Name: Pally Pistao, level: 131
Name: Fryzon, level: 129
Name: Target Down, level: 146
Name: Vinagrete, level: 141
Name: Rapaz, level: 147
Name: Ned Stark, level: 119
Name: Lemysk Suisem, level: 142
Name: Taz Mania, level: 128
Name: Mancha Verde, level: 150
Name: Volna Dream, level: 147
Name: Castiel, level: 149
Name: Sir Druidizinho, level: 128
Name: Paladin Of Holy, level: 138
Name: Devil Oo Barbaro, level: 147
Name: Matheus De Mage, level: 150
 
And how would you detect spoofing? Since you are just getting a player list no?
Maybe you can check for Similar names or somethin'
 
And how would you detect spoofing? Since you are just getting a player list no?
Maybe you can check for Similar names or somethin'
Most of otses that spoof number of players (edit status.cpp) don't login players in game.
You can also analyse who login and who logout and compare it to other otses [how many players login/logout per hour - compare other otses with same number of players online], spoofer probably spoof wrong levels (don' grow like on normal ots).
Of course all these things are only important for OTServ list hosters.

I find out one important thing! :)
I send packet 6,0,255,1,32,0,0,0
but what are these bytes 6,0,255?
It's MESSAGE LENGTH (6,0 = 6 chars [6 * 1 + 0 * 256], our message really has 6 letters 255,1,32,0,0,0) and PROTOCOL (255 = server status protocol), so it can be also:
3,0,255,1,32
(I don't know how server handle that 'short' [32 and 0] has 1 byte [just 32], but somehow it works)
and it's very important if you want get other 'advanced status' (like check if player exists), because if you include name of player at end then length of message will be different for sure.
 
Last edited by a moderator:
For some time I was wonder how it works. But I didn't even looked and this and anywhere. I just didn't care about it until now.
Anyway. I wrote program in java that takes list and info from whoisonline. Maybe in future I will write it to take everything from server.
Maybe I will also upgrade everything to check if there is some kind of "noob spoof". You know, button click and it shows you if there is difference between whoisonline and server status. Or. Idk. Maybe something else.
I need to analyze this and get to know this better ;P
 
Back
Top