# Steam API XML parse into css?



## Solaris17 (Aug 25, 2014)

Hi guys more of a proof of concept im curious about I was looking at hosting a csgo server (irrelevant fun project) which got put on hold when I got interested in the API server status you can run which is this

api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246&format=xml

The issue is this pulls raw xml with no CSS styling and I was simply wondering if it was possible to make a php file that could call that link maybe and parse it into preset css?

This is more proof of concept than anything but im curious how you could parse the xml tags and style them so that if it were embeded into a website it would be presentable data.

the server IP is my own


----------



## Disparia (Aug 25, 2014)

I see that the API also has a JSON output option.

```
$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246';

$server = json_decode(file_get_contents($url), true)['response']['servers'][0];

print_r($server);

/* (
    [addr] => 71.180.229.246:27015
    [gmsindex] => 65534
    [appid] => 730
    [gamedir] => csgo
    [region] => -1
    [secure] => 1
    [lan] =>
    [gameport] => 27015
    [specport] => 0
) */
```

Not that XML would have been a problem.

```
$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246&format=xml';

$xml = simplexml_load_string(file_get_contents($url));

echo $xml->servers->server->addr;
//etc...
```

It could get certainly get more elaborate than that. An AJAX call after the page is loaded could prevent hang-ups if Steam is down or slow.

-edit-

I shouldn't go straight for the ['servers'][0] since I realized now that the server may not be up and the response should be checked first  (it's late, could help ya more tomorrow)


----------



## FordGT90Concept (Aug 25, 2014)

JSON is probably the better way to go simply because it will use less bandwidth.

No matter which you use, make sure to check ['response']['success'] == true before attempting to access the ['servers'] set.  Servers is a list so enumerate through it looking for ['gamedir'] == "csgo"

You'll want to echo the data into HTML with the necessary class and id tags for CSS to work off of.


----------



## Aquinus (Aug 25, 2014)

Theoretically, a library like Enlive for Clojure could transform the XML into an appropriate HTML document, but honestly you should just process the response of the API call and not transform (in the sense of transformations) it into something pretty looking. The prettiness (HTML sent to an end user) and the XML response (coming into the server from Steam's API) are two very different things, try to keep them that way. It would be more wise to have an HTML template that uses the data from said response.

Note: If you wrote an API to turn it into JSON, you could do most of it client side in a language like JavaScript or a derivative.


----------



## FordGT90Concept (Aug 25, 2014)

Just remove the &format=xml and it returns JSON.


----------



## Aquinus (Aug 26, 2014)

FordGT90Concept said:


> Just remove the &format=xml and it returns JSON.


Quick way of saying you don't need a sliver of server-side code to handle this. You can do this with JS and HTML alone.


----------



## Solaris17 (Aug 26, 2014)

Jizzler said:


> I see that the API also has a JSON output option.
> 
> ```
> $url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246';
> ...



if you didnt want to start with servers[0] what would you start with? Can you modify the json output as separate objects like you can with XML I'm not familiar with it but it appears you can but this way it just displays the full array string from $url correct?

infact looking at it it seems to me like it would be logical to break the json up via


```
$server = json_decode(file_get_contents($url), true)['response']['servers'][0];
```

into something like


```
$server = json_decode(file_get_contents($url), true)['response']['addr'][0];
```

going by these catagories


```
<response>
<success>true</success>
<servers>
<server>
<addr>71.180.229.246:27015</addr>
<gmsindex>65534</gmsindex>
<appid>730</appid>
<gamedir>csgo</gamedir>
<region>-1</region>
<secure>true</secure>
<lan>false</lan>
<gameport>27015</gameport>
<specport>0</specport>
</server>
</servers>
</response>
```

but using addr does not seem to work. does it not because it is nested inside of 'server'?


----------



## Aquinus (Aug 26, 2014)

Solaris17 said:


> if you didnt want to start with servers[0] what would you start with? Can you modify the json output as separate objects like you can with XML I'm not familiar with it but it appears you can but this way it just displays the full array string from $url correct?
> 
> infact looking at it it seems to me like it would be logical to break the json up via
> 
> ...



Both XML and JSON represent the data the same. You would still need to take the first <server> tag from <servers>. The code for XML there is misleading because you still need to do something similar with the XML.  I should also add that JSON parsers for many languages tend to be vastly faster than XML parsers.

Is there a particular way you want this done or do you have something specific in mind? I ask this because HTML/JS is the best way to eliminate the middleman (server) for checking status and requires zero server-side code which would make the page incredibly responsive and lean on bandwidth for the server. JSON can make this incredibly easy and efficient.


----------



## Solaris17 (Aug 26, 2014)

Aquinus said:


> Both XML and JSON represent the data the same. You would still need to take the first <server> tag from <servers>. The code for XML there is misleading because you still need to do something similar with the XML.  I should also add that JSON parsers for many languages tend to be vastly faster than XML parsers.
> 
> Is there a particular way you want this done or do you have something specific in mind? I ask this because HTML/JS is the best way to eliminate the middleman (server) for checking status and requires zero server-side code which would make the page incredibly responsive and lean on bandwidth for the server. JSON can make this incredibly easy and efficient.



I simply wanted to parse the data I received via the API into easy to read brackets like

Online:

Server:

Port:

Secure:

So that I could embed it in a webpage that was my whole purpose I just couldn't figure out how to make it "Pretty" The data is functional but it isnt what a normal user would want to see and seeing as I know close to nothing with pulling this data and making it readable I wanted to learn on the way I am at anyone's mercy when it comes to this. The best I can provide is common sense and a logical thought process.


----------



## FordGT90Concept (Aug 26, 2014)

Tweaking Jizzler's code...

```
$ip = 'SNIP';
$appid = 730; // Counter-Strike: Global Offensive

$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=' . $ip;

$response = json_decode(file_get_contents($url), true)['response'];

if ($response['success']) // Make sure Valve was able to query the server.
{
  echo $ip . " Online<br/>Servers:<ul>";
  for ($i=0; $i < count($response['servers']); $i++) // Check each server in the response.
  {
    if ($response['servers'][$i]['appid'] == $appid) // Make sure the host matches requested.
    {
      echo "<li>" . explode(':', $response['servers'][$i]['addr'])[1]; // Get the port number.
      if ($response['servers'][$i]['secure'])
      {
        echo " Secured";
      }
     echo "</li>";
    }
  }
  echo "</ul>";
}
else
{
  echo $ip . " Offline"; // Nothing to see here; move along.
}
```
Code is untested.  Edit the echo lines to make it pretty.  Put a DIV or SPAN around it, put it in table, whatever.

It should look like:


> SNIP Online
> 
> 27015 Secured


In theory, anyway.  If you had two more CSGO servers running on the same IP, one was secured and one wasn't, it should look like this:


> SNIP Online
> 
> 27015 Secured
> 27016
> 27017 Secured


----------



## Solaris17 (Aug 26, 2014)

FordGT90Concept said:


> Tweaking Jizzler's code...
> 
> ```
> $ip = '71.180.229.246';
> ...



Thanks ford  I do have an issue with this version when calling the IP it appears to break it I get the 71.18 echos to the page but nothing else so I imagine its a problem with the first 2 lines?


```
$ip = '71.180.229.246';
$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=' + $ip;
```

but I am unsure if it is a context issue or further down?

nevermind I broke it with a line break that stopped the ?> at the end of the document.


----------



## FordGT90Concept (Aug 26, 2014)

I made a lot of changes.  I was using the wrong character (+) to concatenate strings.  I haven't wrote PHP in forever.


----------



## Solaris17 (Aug 26, 2014)

So basically for JSON in general php has a built in decode flag and you simply make each section a variable?


----------



## FordGT90Concept (Aug 26, 2014)

json_decode processes the entire file into a mixed array.  Do:

```
print_r(json_decode(file_get_contents($url), true));
```
To see the whole array.  We're just jumping to the parts we care about.

simplexml_load_string loads it into a class describing the entire XML so you have to step through the children using -> .  Arrays are easy to work with because you can copy part of it and focus on it easily.


----------



## Solaris17 (Aug 26, 2014)

FordGT90Concept said:


> json_decode processes the entire file into a mixed array.  Do:
> 
> ```
> print_r(json_decode(file_get_contents($url), true));
> ...



So was that why Jizzlers example included

```
echo $xml->servers->server->addr;
```
 ? Am I correct in reading it linear? you MUST do servers->server and not just 'server'?


----------



## FordGT90Concept (Aug 26, 2014)

Yes, because look at the hierarchy of the XML:

```
<response>
	<success>true</success>
	<servers>
		<server>
			<addr>SNIP:27015</addr>
			<gmsindex>65534</gmsindex>
			<appid>730</appid>
			<gamedir>csgo</gamedir>
			<region>-1</region>
			<secure>true</secure>
			<lan>false</lan>
			<gameport>27015</gameport>
			<specport>0</specport>
		</server>
	</servers>
</response>
```
The reason why you can ignore _response_ is because the root element is implicit and required.  $xml effectively refers to _response_ because it always refers to the root element which is _response_ in this case.  You can access _success_ via $xml->success.

There can be more than one _server_ in _servers_.  I suspect Jizzler's XML code would break (or only reach #0) if that were the case).


----------



## Solaris17 (Aug 26, 2014)

I really appreciate the explanation thank you. For those that want to know I did modify the code a bit to make it a little more precise. For example if something is running on the IP valve will still query success and thus the CS server will read as online. However if you query the port it will switch to offline the second the server goes down..or rather when valves API catches up to see if its down.


```
$ip = '71.180.229.246';
$appid = 730; // Counter-Strike: Global Offensive

$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=' . $ip;

$response = json_decode(file_get_contents($url), true)['gameport'];

if ($response['your-port-here']) // Make sure Valve was able to query the server.
{
  echo $ip . " Online<br/>Servers:<ul>";
  for ($i=0; $i < count($response['servers']); $i++) // Check each server in the response.
  {
    if ($response['servers'][$i]['appid'] == $appid) // Make sure the host matches requested.
    {
      echo "<li>" . explode(':', $response['servers'][$i]['addr'])[1]; // Get the port number.
      if ($response['servers'][$i]['secure'])
      {
        echo " Secured";
      }
     echo "</li>";
    }
  }
  echo "</ul>";
}
else
{
  echo $ip . " Offline"; // Nothing to see here; move along.
}
```

EDIT:: nvm this did not work.


----------



## FordGT90Concept (Aug 26, 2014)

Success is based on whether or not api.steampowered.com could invoke a response from 71.180.229.246 at all.  That's why it can be successful but not have any servers available.  If I were you, I wouldn't show anything on success and "71.180.229.246 unreachable" on fail.  The mere presence of an instance under servers says that specific server is online.  If it is offline, it simply won't be in the list.  If you want to simulate your typical offline/online message, you have to check for the specific instance of a server.  If exists, online; if doesn't exist, offline.

I would probably use the gameport field for that.  Also using gameport, you could get rid of the explode bit (silly me).


----------



## Solaris17 (Aug 26, 2014)

FordGT90Concept said:


> Success is based on whether or not api.steampowered.com could invoke a response from 71.180.229.246 at all.  That's why it can be successful but not have any servers available.  If I were you, I wouldn't show anything on success and "71.180.229.246 unreachable" on fail.  The mere presence of an instance under servers says that specific server is online.  If it is offline, it simply won't be in the list.  If you want to simulate your typical offline/online message, you have to check for the specific instance of a server.  If exists, online; if doesn't exist, offline.



Thats what I was looking into. More for education the steam API for example will show success when it can query the IP itself however it will not generate some subsets in this example gameport is not created if the game is not found but the IP is reachable. In my case I attempted to modify it to ask for the gameport group and fail or ask for gameports response IE the port number or fail since this would be even more accurate because even if the IP is infact unreachable gameport would not be polled and the response would still be fail.

at least that was my logic behind it.


----------



## FordGT90Concept (Aug 26, 2014)

I'm pretty sure api.steampowered.com is accessing Steam on the computer hosting.  Success is based on whether or not the API was able to connect.  If it can't connect, there will be no instances of _server_ under _servers_.

In your example above, I believe it would always fail because $response will never contain your port number, always _success_ and _servers_.


----------



## Solaris17 (Aug 26, 2014)

You are right $response wouldnt work but I am pretty sure that the API only polls the IP because it is assuming the server is currently running. As of right now it is not but the IP does repond so it generates a success however server specific fields are not generated since it cannot continue to poll the server port.

http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246&format=xml


----------



## Solaris17 (Aug 26, 2014)

I did manage to make the correct adjustment it seems like this time I used the variable "servers" since the steam API does not create the opening <servers> and only the closing </servers> if you poll "servers" it does not require much modification


```
$ip = 'your-IP';
$appid = 730; // Counter-Strike: Global Offensive

$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=' . $ip;

$response = json_decode(file_get_contents($url), true)['reponse'];

if ($response['servers']) // Make sure Valve was able to query the server.
{
  echo $ip . " Online<br/>Servers:<ul>";
  for ($i=0; $i < count($response['servers']); $i++) // Check each server in the response.
  {
    if ($response['servers'][$i]['appid'] == $appid) // Make sure the host matches requested.
    {
      echo "<li>" . explode(':', $response['servers'][$i]['addr'])[1]; // Get the port number.
      if ($response['servers'][$i]['secure'])
      {
        echo " Secured";
      }
     echo "</li>";
    }
  }
  echo "</ul>";
}
else
{
  echo $ip . " Offline"; // Nothing to see here; move along.
}
```


----------



## FordGT90Concept (Aug 26, 2014)

Here's what I came up with:

```
$ip = 'SNIP';
$servers = array(
		array(27015, 730) // port 27015 should have Counter-Strike: Global Offensive
	);
/* Example of many servers:

$servers = array(
		array(27015, 730), // port 27015 should have Counter-Strike: Global Offensive
		array(7777, 104900), // port 7777 should have ORION: Prelude
		array(26015, 91700) // port 26015 should have E.Y.E: Divine Cybermancy
	);

*/

$url = 'http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=' . $ip;

$response = json_decode(file_get_contents($url), true)['response'];

function IndexOfServer($serverarray, $portappid) // Finds the gameport + appid in the response and returns the index.
{
	for ($i=0; $i < count($serverarray); $i++) // Check each server in the response.  If there are no servers, it will fall through to return -1.
	{
		if (($serverarray[$i]['gameport'] == $portappid[0]) && ($serverarray[$i]['appid'] == $portappid[1]))
		{
			return $i; // I found it!
		}
	}
	return -1; // I couldn't find it.
}

if ($response['success']) // Make sure Valve was able to query the server.
{
	echo '<ul>'; // Start unordered list
	for ($s=0; $s < count($servers); $s++) // Loop through our list of expected servers.
	{
		$i = IndexOfServer($response['servers'], $servers[$s]);
		$secured = ''; // Placeholder for our secured message, if it is secured.
		echo '<li class="';
		if ($i == -1)
		{
			echo 'offline'; // This server that was expected to be found was not so we must assume it is offline.
		}
		else
		{
			echo 'online';
			if ($response['servers'][$i]['secure'])
			{
				$secured = ' Secured'; // Would recommend replacing this with an <img /> of a padlock.
			}
		}
		echo '">' . $servers[$s][0] . $secured . '</li>';
	}
	echo "</ul>"; // End unordered list
}
else
{
  echo $ip . ' unreachable'; // Nothing to see here; move along.
}
```
Use the offline and online classes to decorate the text to show status.  For example, make offline red and online green.


----------



## Solaris17 (Aug 26, 2014)

FordGT90Concept said:


> Here's what I came up with:
> 
> ```
> $ip = '71.180.229.246';
> ...



This one only echos the server port in <li> form

edit:


```
echo '<li class="';
        if ($i == -1)
```
 ? no closing > detected on notepad++?


----------



## FordGT90Concept (Aug 26, 2014)

Can you copy and paste the source it dumped?


----------



## Solaris17 (Aug 26, 2014)

```
<ul><li class="offline">27015</li></ul>
```


----------



## FordGT90Concept (Aug 26, 2014)

Is the server offline?  If it isn't, I suspect it is because $response can't be accessed from inside of the function IndexOfServer().  The solution for this is to either add global in front of $response so that it does or hand off $response['servers'] as an argument of the function.  I'll work on the latter...


Edit: Made the changes to post #23.


----------



## Solaris17 (Aug 26, 2014)

yes the server is online currently why do you think it could not access it some misconfiguration?


----------



## FordGT90Concept (Aug 26, 2014)

Variable scope.


----------



## Solaris17 (Aug 26, 2014)

this instance now shows only the port and secured status line.


```
<ul><li class="online">27015 Secured</li></ul>
```


----------



## FordGT90Concept (Aug 26, 2014)

In the HTML _head_, add CSS for defining the online and offline classes.  Example:

```
<style>
	.offline { color: #800000; } /* Dark Red */
	.online { color: #008000; } /* Dark Green */
</style>
```


----------



## ste2425 (Aug 26, 2014)

I got bored so thought id throw together another implementation, angularjs and nodejs (I love the node  ) and bootstrap. I tried just hitting the steam API just from client angular but got cross-origin access errors. So just put a simple route in the node server that hits it for me and returns it.

Package.json


```
{
  "name": "servers",
  "version": "0.0.1",
  "dependencies": {
    "body-parser": "1.0.0",
    "express": "4.2.0",
    "jade": "1.3.0"
  }
}
```

App.js (Node server)


```
var express = require('express'),
    app = express(),
    http = require('http'),
    bp = require('body-parser');

app.use(bp.json());
app.use(bp.urlencoded());
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');

app.get('/', function(req, res) {
    res.render('index');
});

app.get('/servers', function(req, res) {
    http.get('http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=71.180.229.246', function(response) {
        var body = '';

        response.on('data', function(chunk) {
            body += chunk;
        });

        response.on('end', function() {
            try {
                body = JSON.parse(body);
                res.send(body.response.servers.length > 0 ? body.response.servers : []);
            } catch (e) {
                res.send([]);
            }
        });
    }).on('error', function(e) {
        res.send([]);
    });
});

app.set('port', process.env.PORT || 3001);

var server = app.listen(app.get('port'), function() {
    console.log('Express server listening on port ' + server.address().port);
});
```

and index.jade


```
doctype html
html
    head
        meta(charset='utf-8')
        meta(http-equiv='X-UA-Compatible', content='IE=edge')

        title Server List
        meta(name='viewport', content='width=device-width, initial-scale=1')

        style.
            @import url('http://getbootstrap.com/dist/css/bootstrap.css');
        script(src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js").
        script.
            var myApp = angular.module('myApp', []);
            myApp.controller('controller', function($scope, $http, $interval){
                $scope.servers = [];

                $scope.getServers = function(){
                    $http({
                        method: 'GET',
                        url: '/servers'
                    }).success(function(data, status, headers, config) {
                        $scope.servers = data;
                    }).error(function(){
                        $scope.servers = [];
                    });
                }

                $scope.getServers();
                $interval($scope.getServers, 5000);
            });

    body(ng-app='myApp')
        .container
            h2 Servers
            div(ng-controller="controller")
                table.table
                    thead
                        tr
                            th Address
                            th GMS Index
                            th App Id
                            th Game Dir
                            th Region
                            th Secure
                            th Lan
                            th Gameport
                            th Specport
                    tbody
                        tr(ng-repeat="server in servers")
                            td {{server.addr}}
                            td {{server.gmsindex}}
                            td {{server.appid}}
                            td {{server.gamedir}}
                            td {{server.region}}
                            td {{server.secure}}
                            td {{server.lan}}
                            td {{server.gameport}}
                            td {{server.specport}}
                        tr(ng-show="servers.length == 0")
                            td No servers to display
```

Obviously I wouldn't Use in-line JavaScript like this but couldn't be bothered setting up a public directory in node to server the files, then including them.

It will poll the server every 5 seconds updating the UI. If there's an error anywhere along the chain the server will respond with an empty array. You could use socket.io to have the server push out to all connected clients every 5 seconds rather than every client poll the server. 

You could even cache the response from steam and poll steam independently to the clients polling your server, then you wont hit steam every time clients hit you if you had multiple clients, may speed up the clients request slightly.

and finished product


----------



## Solaris17 (Aug 27, 2014)

FordGT90Concept said:


> In the HTML _head_, add CSS for defining the online and offline classes.  Example:
> 
> ```
> <style>
> ...



I am simply doing this in <?php
?> is that not correct do I have to call the file via html?


----------



## FordGT90Concept (Aug 27, 2014)

That goes inside the head tags of the HTML.  You know:

```
<html>
  <head>
    <title></title>
    <style>
      .offline { color: #800000; } /* Dark Red */
      .online { color: #008000; } /* Dark Green */
    </style>
  </head>
  <body>
  </body>
</html>
```
It can also go in an external CSS.

If PHP files are display any data, it should always be wrapped in HTML so it is compliant with HTML specification.  That way the browser knows for sure what to do with it.  The PHP I gave you should be entirely inside the <body> elements.


----------



## Solaris17 (Aug 27, 2014)

no I mean I am coding this as .php did you want me to impliment this into a .html that calls the php status page?

Or am I just not thinking right? (I have a cold)

oh wow nvm im stupid the issue is even within the <body> and not including the colors in <style> it still only echos the port with its secure status like above.

the source looks like


```
<html>
  <head>
    <title></title>
    <style>
      .offline { color: #800000; } /* Dark Red */
      .online { color: #008000; } /* Dark Green */
    </style>
  </head>
  <body>
<ul><li class="online">27015 Secured</li></ul>  </body>
</html>
```

am I still not formatting this correctly? Please forgive me if I missed it its been hard for me to focus the past few days,.


----------



## Aquinus (Aug 27, 2014)

Just for clarification purposes, if you have a the 
	
	



```
<?php
```
 tag open, you *do not need to close it if your file is strictly a script* and not HTML with embedded PHP. This is commonly done to ensure that a script does not output any white-space at the end of the file as that can cause the web server to send headers before the application is ready. This is something you'll encounter in Moodle source code.


----------



## FordGT90Concept (Aug 27, 2014)

Solaris17 said:


> no I mean I am coding this as .php did you want me to impliment this into a .html that calls the php status page?
> 
> Or am I just not thinking right? (I have a cold)
> 
> ...


I put that in an HTML file and it looks right to me.  The text is green:





You can decorate it however you like.  All the information is being collected and is available.


----------



## Solaris17 (Aug 27, 2014)

FordGT90Concept said:


> I put that in an HTML file and it looks right to me.  The text is green.



but nothing else is supposed to output?


----------



## FordGT90Concept (Aug 27, 2014)

Not according to the PHP.  What else do you want output?


----------



## Solaris17 (Aug 27, 2014)

FordGT90Concept said:


> Not according to the PHP.  What else do you want output?



This is my fault iv been lost for almost the entire conversation I thought this version was simply a diffirent way to display the original

Online:

Server:

Port:

Secure:

with color.


----------



## FordGT90Concept (Aug 27, 2014)

Except for the IP, the code you have is already doing all of that.  If you want the IP, just change the echo 'gameport' to 'addr'.

Can you write the HTML exactly how you want it?


----------

