Halo Demo Server Query

Post a reply


In an effort to prevent automatic submissions, we require that you complete the following challenge.
Smilies
:D :) :( :o :shock: :? 8) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: ----) \ :) / .````:) ?? :( 0) B) O_)
View more smilies

BBCode is ON
[img] is ON
[flash] is OFF
[url] is ON
Smilies are ON

Topic review
   

Expand view Topic review: Halo Demo Server Query

Re: Halo Demo Server Query

by Dirk Gently » Thu Jul 29, 2010 9:42 am

NOOOOOOOOOOOOOOOOOOO UUUUUUUUUUUUUUUUUUUUUUUU!

Re: Halo Demo Server Query

by nil » Mon May 24, 2010 11:06 pm

Just to note: I don't seriously think anyone that can access these boards is a noob. I'm just having a bit of fun here and there.

Re: Halo Demo Server Query

by Altimit01 » Mon May 24, 2010 5:58 pm

Riveting tale chap. I do take offense at your accusation of noobishness but otherwise this is an interesting bit of code.

Halo Demo Server Query

by nil » Sun May 23, 2010 10:04 pm

This forum is getting kind of null and dull, so I figured I'd post something for you zoobs and noobs.

I wrote a tool for ClanDistrict that kindly asks an array of servers (which are stored in a text file) their statistics and then it displays the statistics of the online servers to the user. It sends a packet, with the weirdest possible message possible, to the server and then the server sends back an array of null terminated strings (that I end up separating) of server data, which includes like scores, player names, teams, gametype, etc.

When the tool sends a packet to the halo server, the tool waits for a certain time for the server to respond. I set it to be 350 milliseconds (aka 350 ping). If the server doesn't respond in that time, the tool assumes the server is offline. What is more is that you're noob. If say, there are 10 servers in the list to get statistics from, then the worst case scenario (if they're all offline) is that it'll take 350 ms * 10 ~= 3.5 seconds to query all the servers. Of course, I don't think there's going to be that many very active & reliable clandistrict demo servers anyway, even though I still think 10 servers wouldn't be much of an issue.

Some things this tool does that others don't, and therefore prevails:
*It doesn't assume a player is on red or blue team in a team game.
*It displays sane time scores for team Oddball/King games. Other tools are too lazy or newby to convert halo time into something sensible.
*It reports a list of servers online as opposed to just a single server (the idea of server favorites!)

As a bonus, it links HDemoLauncher server URL's.

Though I may have intentionally decided to leave some information out. I kind of forget. Anyway, HDemoLauncher's querying tool still is better because of its ping estimation and direct connection, but this thing is a lot simpler code-wise and pretty accessible.

It's written in PHP (first time ever using it btw, so if some things suck like me not discovering structures/classes or constants/globals, then get owned). Keep in mind that I wrote halodemoserverinfo.php as a blackbox to ClanDistrict coders/designers so that they could use my code and make the interface/html more pretty than it is right now. Also, I originally was able to figure out all this stuff because someone open sourced his own querying tool. Nifty. Here's some code.

halodemoserverinfo.php:

Code: Select all

<?php
    // Returns an array of servers from servers.txt
    // Each item in the array is an array with keys "address"
    // and "port" (an integer)
    // Address can be a DNS.
    function getserverlist()
    {
        $servers = array();
        $serverindex = 0;
        
        $file = fopen("servers.txt", "r");
        if ($file)
        {
            while (!feof($file))
            {
                $line = fgets($file);
                
                if (!empty($line) && substr($line, 0, 1) != "#" && $line != " " && $line != "\n")
                {
                    $serverinfo = explode(":", $line);
                    $data = array("address" => $serverinfo[0], "port" => intval($serverinfo[1]));
                    $servers[$serverindex] = $data;
                    
                    $serverindex++;
                }
            }
            
            fclose($file);
        }
        
        return $servers;
    }
    
    // address can be a DNS.
    // On success, returns an array with keys:
    // "hostname" (the server name), "gamever" (demo version is "01.00.00.0579"), "maxplayers", "password" (integer, 1 or 0), "numplayers", "gametype", "gamevariant", "teamplay" (integer, 1 or 0), "redscore" (a string), "bluescore" (a string),
    // "gamevariant" (differs from "gametype" when the host creates his own custom gametype), "host", and "players" (an array of player arrays, which each has keys "name", "score" (a string), and "team" (integer, 1 or 0))
    // On failure, returns NULL
    function getstats($address, $port)
    {   
        $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        $stats = NULL;
        
        if ($socket)
        {
            $file = fopen("message", "r");
            $messageToSend = fread($file, 11);
            fclose($file);
            
            // Tell the server we want the statistics
            socket_sendto($socket, $messageToSend, 11, 0, gethostbyname($address), $port);
            
            $sockets = array($socket);
            $write = NULL;
            $except = NULL;
            if (socket_select($sockets, $write, $except, 0, 350000) > 0)
            {
                $actualMessage = "";
                socket_recvfrom($socket, $actualMessage, 1024, 0, $address, $port);
                
                // start at second character, because the first one is a zero
                $startIndex = 1;
                $message = array();
                $arrayIndex = 0;
                for ($messageIndex = $startIndex; $messageIndex < strlen($actualMessage); $messageIndex++)
                {
                    if (substr($actualMessage, $messageIndex, 1) == Chr(0))
                    {
                        $message[$arrayIndex] = substr($actualMessage, $startIndex, $messageIndex - $startIndex);
                        $startIndex = $messageIndex + 1;
                        $arrayIndex++;
                    }
                }
                
                // Grab player information
                $players = array();
                $dataIndex = 39;
                for ($playerIndex = 0; $playerIndex < intval($message[19]); $playerIndex++)
                {
                    $player = array
                    (
                        "name" => $message[$dataIndex],
                        "score" => $message[$dataIndex + 1],
                        "team" => intval($message[$dataIndex + 3]),
                    );
                    
                    $players[$playerIndex] = $player;
                    $dataIndex += 4;
                }
                
                // Score information
                $redscore = intval($message[$dataIndex + 5]);
                $bluescore = intval($message[$dataIndex + 7]);
                
                if ($message[21] == "King" || $message[21] == "Oddball")
                {
                    $redscore = humanizehalotime($redscore);
                    $bluescore = humanizehalotime($bluescore);
                }
                
                // All of the information
                $stats = array
                (
                    "hostname" => $message[1],
                    "gamever" => $message[3],
                    "maxplayers" => intval($message[7]),
                    "password" => intval($message[9]),
                    "numplayers" => intval($message[19]),
                    "gametype" => $message[21],
                    "teamplay" => intval($message[23]),
                    "gamevariant" => $message[25],
                    "host" => $message[39],
                    "players" => $players,
                    "redscore" => $redscore,
                    "bluescore" => $bluescore
                );
            }
            
            socket_close($socket);
        }
        
        return $stats;
    }
    
    // private function, not used by you
    function humanizehalotime($halotime)
    {
        $seconds = intval($halotime / 30);
        $minutes = intval($seconds / 60);
        $time = "";
        
        if ($minutes > 0)
        {
            $time .= $minutes;
        }
        
        $time .= ":";
        
        if ($seconds % 60 < 10)
        {
            $time .= "0";
        }
        
        $time .= ($seconds % 60);
        
        return $time;
    }
?>
index.php:

Code: Select all

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html dir="ltr" xml:lang="en" lang="en">
<head>
    <title>Halo Demo Servers</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>

<body>
<?php
    include "halodemoserverinfo.php";
    
    // Get a list of servers from servers.txt
    $serverlist = getserverlist();

    $numberOfServersOnline = 0;

	// Display stats for each server that is online
    foreach ($serverlist as $server)
    {
        $address = $server["address"];
        $port = $server["port"];

        $stats = getstats($address, $port);

        $host = htmlentities($stats["host"]);

        // Check if the stats exist, and if we're checking a halo demo server
        if (isset($stats) && $stats["gamever"] == "01.00.00.0579")
        {
            $numberOfServersOnline++;
			
            // For HDemoLauncher, create the halodemo:// URL
            $halodemourl = htmlentities("\"halodemo://$address:$port/$host\"");

            $title = "<a href=$halodemourl>" . $stats["hostname"] . "</a> (" . $stats["numplayers"] . " / " . $stats["maxplayers"] . ")";

            if ($stats["password"])
            {
                $title .= " | Private";
            }
            else
            {
                $title .= " | Public";
            }

            $title .= " | " . $stats["gametype"];

            // Only indicate game variant if it's different from the gametype
            if ($stats["gametype"] != $stats["gamevariant"])
            {
                $title .= " (" . htmlentities($stats["gamevariant"]) . ")";
            }

            // Indicate total red and blue scores
            if ($stats["teamplay"])
            {
                $title .= " | <font color=#FF0000>" . $stats["redscore"] . "</font> - <font color=#0000FF>" . $stats["bluescore"] . "</font>";
            }

            Print $title;

            // Print all of the player's in a bulleted list
            Print "<ul>";
            foreach ($stats["players"] as $player)
            {
                if ($stats["teamplay"])
                {
                    // Even in a team game, we can't assume the player is on red or blue team
                    // Look at the phoenix server, the host is usually on an non-existent team,
                    // and doesn't spawn (aka host spectator mode)

                    if ($player["team"] == 0)
                    {
                        // Red team
                        Print "<li><font color=#FF0000>";
                    }
                    else if ($player["team"] == 1)
                    {
                        // Blue team
                        Print "<li><font color=#0000FF>";
                    }
                    else
                    {
                        // No team, green
                        Print "<li><font color=#00FF00>";
                    }
                }
                else
                {
                    // Not in team play, black
                    Print "<li><font color=#000000>";
                }

                Print htmlentities($player["name"]) . " (" . $player["score"] . ")</font>";
            }

            Print "</ul>";
        }
    }

	if ($numberOfServersOnline == 0)
	{
		Print "No servers are online.";
	}
?>
</body>
</html>
A current implementation is viewable on ClanDistrict here. Hopefully, an online server is up.
Related Files

Top