About

All articles, tagged with “web”

One Universe, Many Scales

One epic meta-game design I first remember talking about a decade ago while working on Warhammer Online is the multi-scale online game: a system of interconnected games in which you choose to be a solo operative, work in a small group, or command epic forces or huge space fleets and influence a single universe however you choose to contribute. It’s a recurring game design meme that I’ve talked about several times since and I’m sure many others have had similar discussions around the globe ever since multiple computers were connected together.

So, I’m super excited to see CCP games take a huge step towards realizing this epic game design dream today by linking the internet spaceship game EVE online with the brand new PS3 first person shooter Dust 514. Players piloting giant spacecraft in EVE online will be able to swoop down from space and interact with other players running around on the planet’s surface playing Dust 514. This is very cool.

I’m also very excited that the two games are connected using the CREST API that I helped to build: an API that will be usable everywhere. Not only will you be able to interact with the EVE universe at every scale from the human to the intergalactic, the clients used to connect to the universe will work on every scale of device from phones to desktops and every scale of interaction from web tools built by 3rd parties, through 5 minute casual experiences to epic space battles played out in full 3D over hours and campaigns played out over years.

Today’s connection of EVE and Dust is a historic moment, but it’s just the beginning of what will be a very exciting second decade for the universe of New Eden.

Creatarr

cc image by vdu, j4mie

One of the things I’ve been tinkering with since leaving Linden Lab is Creatarr: a creative, collaborative social game. Creatarr’s goal is to bring some of the magical collaborative creation found in Second Life to a wider audience and to push creativity in social games and the web beyond virtual farming and impact text on animal pictures.

I quickly got busy building the CREST API for CCP and I’m likely to have even less free time when I start my new gig, but Facebook are happy for me to continue to tinker with Creatarr and I like shipping code, so I’ve decided to make Creatarr public and run it for as long as people are playing. There are plenty of rough edges, but there are also some neat ideas and plenty of fun to be had, so head over to creatarr.com and dive in.

Thanks to @mrkemeny, Irene Soler, Chris James, @yandle and @profaniti for helping to build Creatarr.

Adding Vary Header Support To Nginx

Although Nginx supports proxy caching it doesn’t provide support for the HTTP Vary header out of the box. This is a problem if you want to use Nginx to proxy different versions of the same URI which Vary on Content-Language or proxy different representations of a RESTful resource specified via the Accept header.

Fortunately it’s relatively easy to add support for the Vary header using the Nginx Lua module and a small amount of Lua, which is much easier than building and maintaining a 3rd party module and doesn’t greatly impact performance.

First, we define a dictionary in the nginx config which will store a mapping from URIs to Vary headers:

lua_shared_dict uriToVary 10m;

Next we define the default location in the nginx config, which will use the lua ProxyRequest function to make a subrequest to /proxy_request then store the Vary header in the response in the dictionary.

location / {

    # make subrequest to /proxy_request, then store response headers
    content_by_lua '
        local vary = require("vary")
        local response = vary.ProxyRequest()
    ';
}

function ProxyRequest()

    -- make subrequest and capture response
    local response = ngx.location.capture("/proxy_request", {
        method = GetRequestMethod(ngx.var.request_method), body = ngx.req.get_body_data()})

    -- forward HTTP headers from response
    for k,v in pairs(response.header) do
        ngx.header[k] = v
    end

    ngx.shared.uriToVary:set(ngx.var.request_uri, response.header["Vary"])

    -- forward status and body from response
    ngx.status = response.status
    ngx.print(response.body)

    return response

end

Finally, we define the /proxy_request location which will use the lua RewriteCache function to combine the uri with the vary headers to generate the final cache key used by the proxy_cache module.

location /proxy_request {
    internal;

    # set defaults
    set $noCache 1;
    set $cacheBypass 1;
    set $cacheKey nil;

    # rewrite using stored data
    rewrite_by_lua '
        local vary = require("vary")
        vary.RewriteCache()
    ';

    # proxy request
    proxy_cache_bypass $cacheBypass;
    proxy_no_cache $noCache;
    proxy_cache_key $cacheKey;
    proxy_cache API_CACHE;
    proxy_pass $proxy$request_uri;
}

function RewriteCache()

    local varyOn = ngx.shared.uriToVary:get(ngx.var.request_uri)
    local cacheKey = nil

    -- if vary unknown for this uri, bypass cache and do not cache
    if varyOn == nil then
        return
    end

    cacheKey = ngx.var.request_uri .. GenerateCacheKey(varyOn, ngx.req.get_headers())
    ngx.var.noCache = 0
    ngx.var.cacheKey = cacheKey
    ngx.var.cacheBypass = 0

end

function GenerateCacheKey(varyOnStr, requestHeaders)

    local cacheKey = ""
    for part in string.gmatch(varyOnStr, "([^,%s+]+)") do

        if requestHeaders[part] then
            cacheKey = cacheKey .. ":" .. requestHeaders[part]
        end

    end

    return cacheKey

end

The first time a URI is requested the cache will be bypassed, but the Vary header from the response will be stored in the shared dictionary. The second time the URI is requested the cache key will be generated from the URI and the appropriate request headers specified in the vary header and the response will be cached. When the URI is subsequently requested with the same set of headers it will be served from the cache.

Note that when the shared dictionary is full it will evict old entries using an LRU scheme. Nginx will generate “ngx_slab_alloc() failed” errors when this occurs, but these can safely be ignored.

Thanks to @jonastryggvi for working with me on the Vary support and @CCPGames for allowing me to blog about it.

Load Balancing Stateful Services With Nginx

The EVE online network architecture uses stateful proxy servers which manage sessions for players connected to the cluster via the EVE client. The client sends requests to the proxy which are forwarded on to sol servers maintaining the game state and the sols send notifications to the proxy which are sent on to the client.

In developing the CREST API we extended the EVE proxies to talk HTTP, then added nginx reverse proxies to the service to provide SSL termination and caching while shielding the EVE proxies from potentially malicious requests.

So, how does nginx know which EVE proxy to send a request to? In the first instance, it just guesses. We set up a set of proxies and use proxy_pass to have nginx just pick one.

upstream eveproxies {
    # List all eveproxies
}

location / {
    proxy_pass http://eveproxies;
}

The proxy can then use the CCP cluster’s RPC machinery to find the character’s session. If nginx has been lucky the request is processed and the response sent back to nginx and from there to the player. If no session exists for the character on any proxy a new session is created and then the request processed as above. If the character session is on a different node the proxy returns an X-accel response to a location which extracts the correct proxy URI from the path and resends the request.

location ~* ^/internal_redirect/(.*) {
    internal;
    proxy_pass http://$1$is_args$args;
}

The performance of this approach can be greatly improved by caching the mapping of authorization headers to proxies, which can be done using a dict and a small piece of lua.

lua_shared_dict tokenToProxy 10m;

location / {

   content_by_lua '
        -- make subrequest and capture response
        local response = ngx.location.capture("/proxy_request", {
            method = GetRequestMethod(ngx.var.request_method),
            body = ngx.req.get_body_data()})

        -- forward HTTP headers from response
        for k,v in pairs(response.header) do
                ngx.header[k] = v
        end

        -- forward status and body from response
        ngx.status = response.status
        ngx.print(response.body)

        -- cache backend for next request
        ngx.shared.tokenToProxy:set(ngx.var.http_authorization,
            response.header["X-Backend"])
    ';
}

location /proxy_request {

    internal;
    set $crestProxy "http://eveproxies";

    rewrite_by_lua '
        ngx.var.crestProxy = ngx.shared.tokenToProxy:get(
            ngx.var.http_authorization)
    ';

    proxy_pass $crestProxy$request_uri;
}

In a configuration with multiple loadbalancers we potentially have to pay the price of one proxy redirection per nginx process. This could potentially be improved by using a shared cache for the authorization to proxy mapping or by using ip affinity to map all requests from a client to a single nginx box, but in practice where the number of requests from a client is much larger than the number of loadbalancers, this improvement is likely to be negligible.

This mechanism ensures that most HTTP requests go straight to the correct proxy without the load balancers having to maintain any state. A new load balancer can be added to the cluster just be being told the addresses of the eve proxies and will quickly start routing requests to the correct location.

dConstructing Augmented Reality

One of the events that kicked off Brighton Digital Festival was dConstruct, the always thought provoking conference run by clearleft.

As usual I found most of the sessions interesting, but not always relevant as there’s a heavy design rather than development focus. The most relevant talk this year was Kevin Slavin’s final talk, Reality is Plenty, which argued that augmented reality is not the next big thing, just as it wasn’t in 2005.

Despite Kevin having a dig at Second Life and having spent a lot of time working on Augmented Reality with Blast Theory while at Nottingham University, I mostly agreed. While there are definitely use cases which benefit from augmented reality (fighter pilot navigation systems and things like Carbon Goggles which are all about making invisible aspects of objects visible) and virtual reality (simulation and virtual meeting spaces) there are plenty of others which are better served by other interfaces. Environments like Second Life are particularly exciting as they allow people to quickly prototype systems to discover which applications work and which don’t.

With both AR and VR it’s tempting to argue that they allow for intuitive interfaces as they model or overlay the real world: people know how to navigate a 3D space so they know how to use a 3D environment and they know how to use AR as they can see. Anyone who has done their time climbing the Second Life learning curve or trying to use AR to find their way around will know this clearly isn’t true. Apparently more abstract interfaces like maps, which talk to the mind rather than the senses are often much easier to use.

There’s a lot of work to be done to make both AR and VR as easy to use as 2D interfaces, let alone as natural as using real world senses. Now that the huge technical problems around networking virtual environments and tracking real world objects with mobile devices are starting to be solved, it is mostly UI work that needs to be done to make these technologies more widely used.

Even if the UX issues are solved there will still be many cases where speaking to the mind is much better than speaking to the senses.

Introspecting Python Decorators

Over the last couple of years I’ve found myself using python decorators to annotate handlers for web requests more and more, both when using Django and with micro-frameworks like mnml and newf.

Where the same functionality is required for all handlers, or the required functionality can be determined from standard request or response headers, using WSGI or Django middleware is fine, but where the required functionality is varies based on the handler its much cleaner to use a parameterised decorator than poluting the environment or response objects just to control the middleware. Functionality can be added to a framework as a suite of decorators and plugged together in an aspect oriented way like lego to easily build up sophisticated behaviours.

Unlike other mechanisms for implementing macros, templating or aspect orientation that introduce a new language, python decorators are pure syntactic sugar that under the hood are simply rewritten as python expressions:

@requires_oauth_scope("email")
def notify_friends(request):
    pass

Is simply shorthand for:

def notify_friends(request):
    pass

notify_friends = requires_oauth_scope("email")(notify_friends)

This simplicity is powerful as it allows decorators to also be used as normal functions, for example to build up higher level decorators that bundle common decorator configurations, but it also means that decorators potentially interact badly with another powerful Python feature: introspection.

In the above example the undecorated notify_friends function has the __name__ “notify_friends”, but the decorated function has the __name__ “requires_oauth_scope”. When decorators are used extensively, this can seriously impact the usefulness of introspection for debugging or generating documentation.

Decorating your decorators with the functools @wraps decorator, which copies the __name__ of the wrapped function over to the wrapping function solves this introspection problem, but introduces another: the decorators now become invisible to introspection. In the example above the __name__ of the decorated function would now be “notify_friends” as in the undecorated case, but we wouldn’t know that the function had been decorated or not.

A potential solution to this new problem is to store the details about the decoration in another attribute that can be inspected at runtime. In addition to copying over the __name__ attribute, functools.wraps also copies over the target __dict__ by default, allowing it to be used to store information about the decoration and be correctly copied over when decorators are chained:

from functools import wraps

def requires_oauth_scope(scope):

    def decorator(target):

        target.__dict__["my_project_requires_oauth_scope"] = scope

        @wraps(target)
        def wrapper(*args, **kwargs):
            # return target(*args, **kwargs) or FORBIDDEN if token does not have required scope

        return wrapper
    return decorator

By constructing decorators in this way we get the benefits of python decorators and more declarative C# style attributes that are visible to introspection.

Meaningful Choices

On Friday I jumped on the train to London to attend Playful 2010, a one day conference put on by mudlark of World of Love fame. Despite billing itself as a day of cross “disciplinary frolicking” and featuring designers, podcasts, discussions of narrative, iphone augmented paper games and Disco Snake the thing that stood out for me was a thread running through the talks that addressed a fundamental of game design: meaningful choices.

Jonathan Smith talked about the dangers of giving people too much freedom in his talk about the Lego Games. Lego is almost a shorthand for freedom: the easy to understand system of knobs and anti-knobs that allows 2 4x2 blocks to be combined in 9 million ways an ultimate sandbox aspired to by games and virtual worlds like Second Life. This open, free system led Travellers Tales to add lots of open, free features to it’s early Lego games that were largely ignored by players who need boundaries and feedback from the game to determine ‘what I want versus what’s expected of me’. Choosing freedom and rebellion is more meaningful when it is clear that I am exercising my freedom and not doing the expected.

Margaret Robertson talked about and in the current sandbox game du jour, Minecraft, which has enough terror and threat in its horror filled night to make the choices made during the day meaningful and to reward mastery of it’s sandbox — a sandbox that compelled Margaret to stay up until early in the morning carving her slides out of earth, building them out of wood and animating them with flowing water and flames burning down the assertion that “games = points”.

It was this misguided assertion that Sebastien Deterding talked about in his look at the ‘gamification’ of the world around us. When all that gamified web sites like foursquare do is allow the accumulation of points and badges there are no meaningful choices, no mastery, no way to rebel against expectations, no play and no fun. Gamification results in loyalty schemes that are no more meaningful than Progress Quest.

The importance of being able to rebel against expectations was echoed by Alexis Kennedy’s talk about delicious misery in Echo Bazaar, a social game that would be another meaningless progression to inevitable success if it weren’t for contrarian missions that allow players to opt-in to getting their characters exiled for scandal or driven insane by demons. These missions inflict real harm on characters, but when properly signposted are the most enjoyed and shared missions: allowing players to be badass. When a game makes success inevitable, misery and failure is play and meaningful escape.

Pat Kane, formerly of Hue and Cry and more recently author of The Play Ethic gave a fascinating talk about wordplay, humour and his journey from disillusionment at the comedy industry, to fascination with humour through the Old Jews Telling Jokes’ stories of Jews laughing in the face of persecution. When misery and failure is inevitable, humour and play is rebellion. An ultimate, meaningful demonstration of freedom and humanity when all hope of victory is gone.

HTML 5 multimedia

I’ve been morbidly fascinated by the Rich Internet Application technology blood bath for a while now: Whirled,Metaplace and others tried to stuff virtual worlds in to web pages using Flash, Second Life stuffed Flash in to virtual worlds via Webkit, Unity stuffed Mono in to a 3D engine and then took on the world and Silverlight and Moonlight stuffed the CLR in to web browsers and Erik Meijer stuffed a CIL interpreter straight in to Javascript.

All good fun and there are fortunes to be won and lost to be sure, but the smart money seemed to be on waiting for the dust to settle and then using the winning technology. Recently, however, amazing technologies like V8, Node.js and the resulting browser Javascript arms race have been adding weight to the Google viewpoint that all you need is Javascript: a philosophy made more pragmatic by Apple’s decree that all you get is Javascript.

A week or so ago I decided to test the hypothesis by building a drum machine using only HTML 5 and Javascript. My first discovery was that while the canvas element is perfectly capable, it’s a very low level API, even for building something as rudimentary as a step sequencer interface. After looking at a number of drawing libraries I settled on processing.js as a higher level drawing API, something I’ve been meaning to play with since we used it to build SLorpedo at Hack Day a few years ago. Processing.js is a neat hack, that despite an incomplete API and some subtleties around casting does a great job of running processing sketches within a browser without a plugin. It also uses a sloppy parser enabling you to drop arbitrary Javascript in to your processing sketch, which makes it easy to just create Audio() objects within the sketch to playback audio. Unfortunately while it was easy to add audio playback, the playback itself was pretty disappointing: Firefox just spluttered and belched sadly while Safari did a decent job of playing beats for a couple of minutes before its timing went to hell and then the browser crashed. The shiny future may yet be HTML 5 and Javascript, especially when the experimental extensions to Firefox become widely supported, but we’re not there yet.

To experiment for yourself, click on the squares on the grid below to add beats to the sequence. To see the code, view source.

@scalecamp

On Friday I jumped on the train to London to attend the first scalecampuk, an unconference about scalability, at the Guardian offices.

The sessions were all very interesting and mostly very relevant. I learned new things about XSS and CSRF and Django’s defences against them from Simon Willison, new things about messaging, pubsub, queues and data stores (process 1 message at a time, use message hospitals, send URLs to unavailable data that can be polled for with JavaScript and that just check memcache entries) lots about Varnish ) and it’s use at Wikia from Artur Bergman (Wikia runs off 18 apaches and 8 varnishes with 60GB of RAM and SSDs to serve 25 million pages and 950Mbps at peak, Varnish is generally better than squid ), but you need a modern kernel).

Lots of the talks were about moving storage, caching and queuing out of the application and just writing a small piece of business logic to tie them together. Against this background Alex Evans’ talk about the back end for Media Molecule’s Little Big Planet stood out like a sore thumb. Having not enjoyed using a Java web stack, Alex has just rewritten the whole of the back end as proprietary technology as a single binary in order to know the code from end to end. While it may be true that having custom physics or rendering middleware might distinguish Little Big Planet from other games, I can’t believe that custom technology to serve HTTP requests is going to be a competitive advantage. I hope Alex’s good ideas become Redis contributions rather than a maintenance nightmare and barrier to agility.

The lightning talks were also very good. Simon’s “ScaleFail” talk about the Guardian MP expenses app was hilarious (lesson: do load testing) and Gareth’s talk about Dumbo (a Python Hadoop client) was very useful.

At times it felt like the talks were suited to an ops audience, but “Dev’s should know about this!” was a regular refrain. Don’t worry: I listened and learned a lot. Thanks to everyone who made it a great day.

Like Second Life

Was without a doubt the phrase I heard most often yesterday, especially if you include variants like “Not Like Second Life”, “A bit like Second Life” and “Unlike Second Life”. Whatever else it’s achieved, Second Life has definitely become the frame of reference for the small and somewhat myopic crowd that made up the delegates at the sparsely populated Virtual Worlds Conference in London yesterday.

Vastpark is not like Second Life because it works in a web browser. Everyone on the web integration panel seemed to agree that virtual worlds in a browser is the next step, so I was glad to be there to question the TechCrunch consensus. How does having a world in a browser help? What does back and forward mean to a virtual world? What does it mean for presence to have 10 tabs open looking in to different parts of the same virtual world? Why would you want your view further constained by extra web browser widgets? Isn’t 3D in the browser going to be a blood bath for the next few years? Aren’t you really just using the browser as a download path? I suggested that the final question was the real reason that developers are pushing virtual worlds on the web and that the integration that most people want is to be able to use existing web and 2D media while using virtual worlds and use web services as a universal data bus between virtual worlds and other web aware platforms.

MPEG-V is not like Second Life because it’s a standard defined by 35 companies which is much better than the emerging Linden led standard according to Dr. Yesha Sivan in what was the worst talk I’ve heard in a long time. Not only did he make the standardisation process sound like a 3 year political bun-fight by people who didn’t know much about virtual worlds and who might come up with a bad standard, he managed to spell MPEG and Google incorrectly, called Sun’s Darkstar, Blackstar and attributed a Ugotrade quote to Philip Rosedale amongst other clangers. He was roundly rebutted by a large part of the audience including Tara5 Oh who questioned the need for old fashioned standards processes in the web era. Thank goodness for rough consensus and running code.

Most of the virtual worlds talked about in the investment panel were not like Second Life, but were nearly all Club Penguin clones. This copy the big exit attitude was called out by one of the audience as it seemed to be at odds with a lot of the talk about wanting to back the first in a market, but at least one of the panel is still looking for a successful 18+ social world play. The panel ended with a show of hands from people wanting money and people wanting to invest, but the economic climate made the whole affair very muted with lots of the panelists saying that they are slowing down rates of investment as it’s difficult to get existing companies off their books.

As with Virtual Policy 08 and the Virtual Worlds Forum the most valuable parts of the conference were the spaces between sessions. I had another very worthwhile discussion with Adam Frisby of OpenSim about C# script compatibility between OpenSim and Second Life. The straw man design we talked about was to have an idiomatic .NET interface for event handling that can be used by C# scripts and adapted for LSL scripts and a set of static library methods for manipulating the world that would be used directly by LSL scripts and wrapped by user created libraries to provide an idiomatic object oriented interface. Adam was particularly interested in the idea of user created wrapper libraries as it would allow the creation of an OpenSim interface library that could be ported to Second Life and implemented in terms of the ll* static methods. OpenSim could then agree to support the common behaviour of this library in Second Life and OpenSim instead of having to support the gamut of ll* methods some of which don’t map well to OpenSim internals. As well as defining a common set of events and ll* static methods that are supported on both platforms there would need to be a way of extending the interface with new events and library methods. In addition Adam was interested in making the event propogation configurable so that a single script could respond to events on many objects in a scene. This would effectively add a script interest management layer to OpenSim’s scripting interface. Where platforms provide differing interfaces to scripts we would also need to decide how scripts query the available interfaces or how they behave when interfaces are not available.

Overall a worthwhile trip, but not because of the conference. This Friday I’ll be talking at the online head conference about conferencing in Second Life which has the advantage of requiring no travel making marginal conferences like the Virtual Worlds Conference less risky to attend while allowing all of the serendipitous networking opportunities that make real life conferences worthwhile.