Did you know: programmers convert coffe to code?

If you like my articles, support me with a small amount.


Buy me a coffee

The Diablo 3 API

After telling in two articles that SOAP is out and REST is in, I think it is time to introduce some REST services and an application to access them. The scariest thing in that I’ve read the first articles about REST (around 2009) when it came out into the market but never ever written a REST client (neither a server). But this will change now, five years later.

For this article I’ll look at the Diablo 3 API with Python.

Blizzard offers a great documented API for their Diablo 3 here. The only thing you have to be aware of is the host: it can vary from region to region. I’ll use eu.battle.net because my character resides in the EU and so it is the way I can access my data.

A quick look at the API

The simplest to examine the BattleNET API is to call it directly from your browser. For this enter simply the following:

http://eu.battle.net/api/d3/profile/Straton-1/

Where eu is the server location:

  • us for the USA
  • eu for Europe
  • kr for Korea
  • tw for Taiwan
  • sea for Southern Asia
  • for China I did not find an API

Straton is the battle tag name and 1 is the battle tag ID.

If the profile is not found you get an according error message as the result:

{
"code" : "NOTFOUND",
"reason" : "The account could not be found."
}

If you’ve chosen the correct server with existing profile name and ID you get a lot of data in return. This makes it complicated to examine the data so you need an application to prepare the JSON dataset to be read.

Rest in py-ces

Because REST is much simpler than SOAP (no need for specific XML structure, no Envelops no parsing — you only have to call a URL with the specific data and REST in peace) there is no need for any specific library to call a REST service.

For this you can use the url lib module because you do not need customized headers (which is supported by urllib2). Or alternatively you can use httplib2 to manage HTTP requests.

example

Let’s have a look at a simple example. I’ll call the same REST endpoint with urllib and httplib2.

>>> import urllib
>>> url = 'http://eu.battle.net/api/d3/profile/Straton-1/'
>>> response = urllib.urlopen(url).read()
>>> print response
{
"code" : "NOTFOUND",
"reason" : "The account could not be found."
}
>>> import httplib2
>>> h = httplib2.Http(".cache")
>>> (resp_headers, content) = h.request(url, "GET")
>>> print content
{
"code" : "NOTFOUND",
"reason" : "The account could not be found."
}

Here you can see that the two calls yield the same result so it is up to you to choose between the libraries. If you plan to use your client on the Google App Engine I suggest you to use urllib because it can be used with the GAE right away, httplib2 needs to be copied beside you application.

The only thing you need is a library to parse JSON messages. And Python comes with one, it’s the json library. To convert the result to an object, you can simply execute the following:

import json
>>> content_conv = json.loads(content)
>>> print content_conv
{u'reason': u'The account could not be found.', u'code': u'NOTFOUND'}
>>> print content_conv['code'], content_conv['reason']
NOTFOUND The account could not be found.

The json.loads method converts the resulting String into a dictionary which you can use as any other Python dictionary.

Requesting Diablo 3 data from Python (aka the example code)

This part will mix together the first two parts from the API and the REST in Python.

Well, working with JSON objects is not a big thing. However if you get a lots of data as from the D3 API you can end up confused. And this is a problem, that’s why I started working on a converter utility to convert the resulting JSON objects to Python classes. You can find the wrapper and the API caller scripts at my GitHub repository.

Converting the JSON result to a class or other object is not tricky: you can either create a method and pass the JSON object as an argument to it or you can make this automatically with the json.loads method defining an object_hook which then parses the JSON object to the desired result.

# ... some code omitted
def convert_to_profile(json_object):
# ... come code omitted
user_profile = json.loads(response, object_hook=convert_to_profile)

Of course there are other implementations of this what I’m doing. For example sammyrulez did it two years ago. However as I look at the code it is not fully. However I do not think that my version will be a full implementation too but I’ll give my best to get a lot done. I could have forked his version however it would only be a burden to work along a previously worked path.

My version can be found at my GitHub repository. As for the most public API wrappers it is mostly a one-time version, provided to you. Eventually I’ll improve it sometime (if I get to know about changes in the API or kind of stuff like that) but it’s staying as it is.

The whole wrapper was challenging, deciding if lazy-loading is a good option or not, or should I use classes or dictionaries… However there are no right or wrong paths everything is fine. A challenge are the raw attributes of a item: they store the numerical data of modifiers: extra damage, resistance, armor, and they come as a dictionary with a whole lot of keys. Every enchanted property gets its unique part, for example

Item_Power_Passive#ItemPassive_Unique_Ring_651_x1

Or the damages, they get their element modifier after a hashmark (#) same as the enchanted attributes:

Resistance#Poison

And this is what it makes difficult to convert these raw attributes to fields of a class — however it makes it hard to get them from a dictionary too. Here I have no idea currently how to map them to the classes.

And this diversification of properties makes it hard to create a calculator which gives you the exact DPS (or any calculated property). Some older apps which were implemented for the original version (without the RoS extension pack) can malfunction because of the Mystic and her special attribute names. Besides this the calculation is not easy because you have to deal eventually with the passive skills too — but as long as it works in the game itself I’m satisfied.

Nevertheless my wrapper should have a usable state where you could build a site (or app or anything) to display a user profile with its heroes, a hero with its items (each at the right body part) and the texts of the items (i.e. “+356 vitality”).

Conclusion

REST is an easy to use protocol, simpler than SOAP. And most of the good applications and sites provide a REST API to use (or eventually play with it). Have you taken a look at the Marvel API? 🙂 (however the API of Marvel isn’t totally public: you have to register to get an API key)

GHajba
 

Senior developer, consultant, author, mentor, apprentice. I love to share my knowledge and insights what I achieve through my daily work which is not trivial -- at least not for me.

Click Here to Leave a Comment Below 0 comments