Amazon Game Tech Blog

How to Deliver Custom Game Content to Players using Lambda@Edge

Amazon Game Tech

When your mobile game client starts, you want it to always reach out and grab the latest and greatest content, right? That way, when you make changes and introduce new features, you can always put them in the hands of your players.

But you can’t always guarantee that all your players have updated to the latest version of your game. So, how do you enable these older game clients to get data from your previous game format? And, since data is checked and downloaded every time someone runs the game, how do you ensure lightning speed delivery, no matter where your players are?

What to do?

There are a lot of different solutions to the problem! Most solutions involve running some sort of web service that will look at incoming requests and run some code to determine what file to serve up.

However, that need for “lightning speed” complicates matters a bit. If a player is halfway around the world, how do you make sure they don’t have to wait for their game to load because a file is downloading over an internet soda straw running under the ocean?

If you’re familiar with web technology, part of the solution is obvious. You need to have that data on a CDN (content delivery network).

A CDN is system where multiple servers around the world distribute your content, ensuring they always have the most current files by checking in with a “master” server. Having servers located around the globe means they’re more likely to be close to players, making the download much faster.

But, a CDN doesn’t completely solve the problem. Because one URL is being hit, there needs to be some code that determines what file to actually send. You don’t want to slow things down by having back and forth time between a server and client. And you can’t run your code on a CDN, it’s a data service right? Or can you…

Life @Edge

AWS offers a fast, highly secure and programmable content delivery network (CDN) called Amazon CloudFront. CloudFront has a very interesting feature called Lambda@Edge that helps solve this problem.

AWS Lambda is a service that lets you run code without provisioning or managing servers. If you’re not familiar with AWS Lambda check out our “Getting Started” article. Lambda@Edge brings this serverless technology to CloudFront.

Lambda@Edge functions run on the same nearby server that is currently serving content to your player. This means that you can decide what CloudFront sends to a player as they’re grabbing data, preventing any additional wait time from talking to a server before sending content.

While Lambda@Edge does contain restrictions on the types of triggers (all centered around CloudFront) and have a shorter allowed running time, it does add a level of utility to CloudFront that isn’t found in a purely static CDN.

Using Lambda@Edge

To help you quickly get to grips with this tool, we’ve created a quick start guide, and a game sample – available for download now.

Please note: Running this sample will incur a charge as there is currently no free tier for Lambda@Edge.

First, we need to talk about what an “origin server” and an “edge server” is.

An origin server is where our files originate from. This is the authoritative location where the latest version of the file resides. To get data into CloudFront, you don’t need to push data to all servers, instead you put it on the origin server.

All of the servers in the various regions that will serve your data to players are called edge servers. The edge servers will always check to see if there is new data on the origin server before sending it to your players.

The edge server will also cache that data. This means the next time it gets hit, data will be ready for the player and there is no need to grab it from the origin again. This is where the speed comes in. By default, the cache will remain for 24 hours, but you can adjust that to be longer or shorter depending on your needs.

So, let’s set up our origin server.

Setting up the origin server

CloudFront can use either your own web server as the origin, or it can use Amazon S3. We used S3 for this demo as it’s simple to configure and upload to:

  1. Go to the AWS control panel and search for S3.
  2. Create a new bucket in Amazon S3.
    • The bucket name must be unique to all of Amazon S3. We chose cfdemo.amazongametech.com.
    • Use the default configuration options.
    • The permissions are going to remain all private as well. We only want to grant public access to our files from CloudFront, not from S3.
  3. Now upload the files
    • First, create two folders in your bucket called “OldFormat” and “NewFormat“.
    • Go into each folder, click Upload and upload the appropriate files found in our GitHub project.
    • Leave the privacy settings alone. Again, these won’t be public from S3, only from CloudFront.

Setting up CloudFront

Here we’ll make what’s called a “distribution”. This is a way to specify how the edge servers will do their work. We then deploy that distribution to actually get the edge servers to do their thing.

  1. Go the CloudFront console
  2. Click Create Distribution.
  3. Choose Web delivery method.
  4. In the origin settings, if you click the Origin Domain Name you will be shown a drop down of all the resources that can be accessed, so we’ll pick the S3 bucket we just created.
  5. Leave the default cache behavior settings.
  6. Adjust the distribution settings:
    • For the demo, we will make this for global customers and choose use all edge locations. If your market is more regional, you could save money here by limiting the scope of the edge distribution to the regions your game serves.
    • It would also be possible for you to use your own domain name to serve requests, but for this demo, we’ll leave that alone as customers won’t see the domain name as the request is made through code in the game client.
  7. Create the distribution.
  8. Go have a cup of tea. This process will take 20-40 minutes to deploy, but eventually you’ll see the status change from In Progress to Deployed.
  9. Once the distribution is deployed we need to make an origin access identity to allow CloudFront to access our private files in Amazon S3.
    • The idea here is that since we’re going to be using Lambda@Edge to select the correct file, we don’t want anyone to inadvertently publish a link to our files in Amazon S3.
      • Click the distribution.
      • Click the Origins and Origin Groups tab.
      • Choose the origin you made earlier and then edit.
      • Choose yes for Restrict Bucket Access.
      • Select create a new identity if it’s not already selected.
      • Click the update bucket policy button to automatically give the S3 bucket permission for the new identity.
  10. Once this is done, you can see the URL that CloudFront assigns to this distribution. This maps to the top level of our S3 bucket. You can test to see if everything worked by using hitting that URL to verify the folder and name of the files we uploaded. For example if you hit: http://a123abcdef1abc.cloudfront.net/NewFormat/some_data_file.dat. you should see the file is downloaded.

Create the Lambda@Edge

  1. Currently Lambda@Edge functions can only be defined in the US-East-1 (N. Virginia) region, so you’ll need to change that at the top left of your console if you’re in a different region. But the only usage and charges will come from the functions actually replicated at the edge servers.
  2. Open the Lambda console.
  3. As we mentioned earlier, there are four different triggers you could choose. We’ll focus on one which is triggered when CloudFront gets a request from the player called a viewer request trigger, meaning they’ve requested a URL.
  4. Click Create function.
  5. Choose the “Blueprint” box.
  6. Search for “cloudfront-ab-test” as our code will be based on similar principles as an AB test run from CloudFront (another great use case for this service by the way!)
  7. Give the Lambda a name that will make it obvious what the function does.
    • We called ours DeliverVersionAppropriateContent.
  8. We need an IAM role to give the Lambda permission to run, so choose Create a new role from one or more templates and then name the role
    • Again use a name that you can tell what it’s for. We chose LambdaEdgeDemoRole
  9. The policy template is already chosen because we chose a blueprint. Handy!
  10. Create the function.
    • We’ll add the code later, for now it will create it with the blueprint’s AB test code.
      The correct trigger is already selected due to choosing the AB blueprint, but you can verify it’s a CloudFront trigger.
  11. Click the box with the Lambda icon and the function name to edit the code.
  12. We will code this in Node.js as it’s already partly implemented and we have easy access to the methods we will need.
  13. The code will mess with the request. Read this guide for information on what the request looks like.
  14. Paste in the following code:
'use strict';    
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.uri !== '/some_data_file.dat') {
        // for the demo we only want to deal with requests for this file
        callback(null, request);
        return;
    }

    const pathNewData = '/NewFormat';
    const pathOldData = '/OldFormat';

    let revisedUri;
    
    const querystring = require('querystring');
    var queryObj = querystring.parse(request.querystring)
    if('version' in queryObj) {
        if(parseFloat(queryObj.version) > 10.1) {
            revisedUri = pathOldData + request.uri;
        }
        else {
            revisedUri = pathOldData + request.uri;
        }
    }
    else {
        // assume old format before we started passing in the version
        revisedUri = pathOldData + request.uri;
    }

    request.uri = revisedUri;
    console.log(`Request uri set to "${request.uri}"`);
    callback(null, request);
};
  1. Now that the code is done, we need to deploy it to the edge server. On the designer portion of the Lambda, click the trigger (which says CloudFront global deployment pending)
  2. Click Deploy to Lamba@Edge in Configure triggers
  3. In the next box, change the CloudFront event to ViewerRequest
  4. Leave the other configuration default
  5. Click the acknowledge checkbox and then Deploy
  6. Wait a few minutes until this is deployed and then test with a browser to see if it worked.
    • You can see in the code we check for a version parameter that the client sends in the URL.
      for example, http://a123abcdef1abc.cloudfront.net/some_data_file.dat?version=9.0 (or leaving off the query entirely) should return the old format and http://a123abcdef1abc.cloudfront.net/some_data_file.dat?version=11.0 should return the new format.

The game client

Using the sample game client in our GitHub project you can download the file to see to verify if it has worked. As mentioned above you can also verify it with a web browser.

The client is very simple. It initializes the AWS C++ SDK and then based on user input of a version number, the client makes a simple HTTP get request and displays the appropriate version file.

When you are done playing around with this demo, I suggest you delete all resources you created (especially the CloudFront distribution and the Lambda) as we don’t want you to incur any unneeded charges.

And that’s it! For more information on Lambda@Edge read the documentation. There are also other useful examples on using Lambda@Edge in this documentation page.

Questions? Get in touch via the Amazon GameDev forum!