bw logo

Chapter 2. The TwistedWeb Service

It is often useful to create a Web Service interface to a BigWorld server. This allows other standard services to be used to access game functionality via standard HTTP requests.

One way to expose a web service interface is to use the TwistedWeb Service provided. This uses the Twisted Python framework and its Twisted.Web module to map HTTP requests to script calls on game entities. Any of an entity's methods can be called on it in this way.

See http://twistedmatrix.com/documents/current/web for more detailed information on Twisted.Web.

A BigWorld Service is a scripted object like a Base-only entity. See Server Overview's section Design Introduction for more information.

BigWorld provides a standard TwistedWeb Service located at bigworld/res/scripts/service/TwistedWeb.py. This service listens for HTTP requests on port 8000. It supports four types of URL paths.

  • db/<dbCommand>?<arguments> - This is used to invoke a command on the database such as logging on a player's entity.

  • entities_by_id/<entityType>/<databaseID>/<methodName>?<arguments> - This calls a method on an entity.

  • entities_by_name/<entityType>/<entityName>/<methodName>?<arguments> - This calls a method on a named entity.

  • global_entities/<globalName>/<methodName>?<arguments> - This calls a method on an entity in BigWorld.globalBases.

The responses to these requests are structured as JSON documents.

2.1. An example

For example, in FantasyDemo, the method webTestMethod for the global entity AuctionHouse is used to test the functionality of method calls using Twisted.Web.

In fantasydemo/res/scripts/entity_defs/AuctionHouse.def, the method is declared as:

...
<BaseMethods>
  ...
  <webTestMethod>

    <Args>
      <first_arg> INT32 </first_arg>
      <second_arg> STRING </second_arg>
    </Args>

    <ReturnValues>
      <first_result> INT32 </first_result>
      <second_result> STRING </second_result>
    </ReturnValues>

  </webTestMethod>
  ...

And in fantasydemo/res/scripts/base/AuctionHouse.py as:

class AuctionHouse( ... ):
    ...
    def webTestMethod( self, first_arg, second_arg ):
        return (2 * first_arg, second_arg.upper())

An instance of AuctionHouse has been registered with BigWorld.globalBases as 'AuctionHouse'.

Therefore, requesting:

http://machine_name:8000/global_entities/AuctionHouse/webTestMethod?first_arg=5&second_arg=Test

returns the JSON object:

{ "first_result": 10, "second_result": "TEST" }

2.2. Queries to /db/

There is currently only one command supported by the /db/ path. This has the form:

http://machine_name:8000/db/logOn?username=username&password=password

This attempts to log on the user. On success it returns the new entity's type and database id. For example,

{ "type": "Account", "id": 2 }

This information can then be used to make queries on this entity using the path starting with /entities_by_id/<entity_type>/<database_id>/<methodName>.

2.3. Queries to /entities_by_id/

Queries of the form /entities_by_id/<entity_type>/<database_id>/<methodName>?<args> can be used to call methods on a specific entity.

For example, requesting:

http://machine_name:8000/entities_by_id/Account/2/webGetCharacterList

might return:

{ "characters": [{"type": "Avatar", "databaseID": 1, "realm": "fantasy", "charClass": "ranger", "name": "MyChar"}] }

The details of one of the characters on this account can be retrieved using http://machine_name:8000/entities_by_id/Account/2/webChooseCharacter?name=MyChar&type=Avatar:

{ "type": "Avatar", "id": 1 }

2.4. Queries to /entities_by_name/

These queries are similar to entities_by_id expect that the database string identifier is expected instead of the database id. The entities_by_id form is preferred as queries by name need to query the database each time while repeated queries via database id will likely hit a local cache of the entity's mailbox on the ServiceApp. See KeepAlive messages below.

2.5. Queries to /global_entities/

These queries allow calling methods on a base entity that has been registered with BigWorld.globalBases. The base entity must be registered with a single string as the key. These queries have the form:

http://machine_name:8000/global_entities/<global_key>/<methodName>?<args>

2.6. Implementation details

The TwistedWeb service is defined in bigworld/res/scripts/service_defs/TwistedWeb.def, and its methods are implemented in bigworld/res/scripts/service/TwistedWeb.py. Here, the resource tree is built up using Twisted.Web's putChild function, which takes as arguments the name of the path segment and the type of the resource that will be returned by a request for it:

from TWResources.EntitiesResource import EntitiesByNameResource, EntitiesByIDResource
from TWResources.GlobalEntitiesResource import GlobalEntitiesResource
from TWResources.DBResource import DBResource

class TwistedWeb( BigWorld.Service ):
    def __init__( self ):
        root = resource.Resource()
        root.putChild( "entities_by_name", EntitiesByNameResource() )
        root.putChild( "entities_by_id", EntitiesByIDResource() )
        root.putChild( "global_entities", GlobalEntitiesResource() )
        root.putChild( "db", DBResource() )

        reactor.listenTCP( 8000, server.Site( root ) )
        reactor.startRunning()
  
    def onDestroy( self ):
        reactor.stop()

The various resources used by the TwistedWeb service are implemented in the TWResources package, which is located at bigworld/res/scripts/service/TWResources.

In order to make use of the TwistedWeb service, it must be given an entry in the <res>/scripts/services.xml file in your project directory:

<root>
    ...
    <TwistedWeb/>
</root>

This file contains a list of all Services that will be initialised when a ServiceApp process is started.

See http://twistedmatrix.com for more detailed information on the Twisted Python framework.

2.7. TwistedWeb Error handling

Two-way calls to the game server using the TwistedWeb service will always return a JSON object. If an error occurs, the object that is returned will have a specific error format. It will consist of two fields: a string named excType containing the error type, and an array of strings named args containing the arguments. The returned document also uses the HTTP error code 403 (Forbidden).

For example, the sample /db/ queries given in section Queries to /db/ could fail in a number of ways. If the account does not exist on the server, the call will return:

{ "excType": "BWAuthenticateError", "args": [ "No such user" ] }

If the password is invalid, it will return:

{ "excType": "BWAuthenticateError", "args": [ "Invalid password" ] }

The regular format of error objects returned from TwistedWeb means that differentiating them from successful return objects only requires the caller to check for the existence of the excType key.

For details about the different types of errors that can be returned by a two-way call, refer to theServer Programming Guide's sectionBWStandardError.