Halo Demo Server Query
Posted: 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:
index.php:
A current implementation is viewable on ClanDistrict here. Hopefully, an online server is up.
Related Files
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;
}
?>
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>
Related Files