Simplifying Google Play Games API

Previously we covered Using Google Play Games on the Web and how Google Play Games services[3] can be used for web games[1]. There was a lot of interest on that article, mostly about providing UI components, which is a project that I have started, when I am not working on the refactor of Gaming Engine - Snake Demo v2. However, before building a UI, the API needed to be cleaned up, making the authentication easier and chainable callbacks without needing function nesting. The whole framework will be available on github[2] as I work on it, but for starters lets explore version 0.1, The API Improvements.

How do it…

From Using Google Play Games on the Web we setup a page with the necessary meta tag variables and a login button:

<meta name="google-signin-clientid" content="YOUR_CLIENT_ID_HERE" />
<meta name="google-signin-cookiepolicy" content="single_host_origin" />
<meta name="google-signin-approvalprompt" content="auto" />
<meta name="google-signin-callback" content="YOUR_JS_CALLBACK_FUNCTION" />
<meta name="google-signin-scope" content="https://www.googleapis.com/auth/games" />

<span id="signinButton"><span class="g-signin"></span></span>

And then add in the following JavaScript from the github project[2]:

<script src="{PATH_TO_JS}/underscore.js"></script>
<script src="{PATH_TO_JS}/api.js"></script>
<!-- This is dumb, but the callback function must be directly attached to window. -->
<script>YOUR_JS_CALLBACK_FUNCTION = GoogleGamesApi.authCallback;</script>
<script src="https://apis.google.com/js/client:platform.js"></script>

Using the following method to wait for authentication:

GoogleGamesApi.runWhenAuthenticated(function(oApi) {
  // Your code here.
}

Now methods on the API can be called to fetch data:

GoogleGamesApi.runWhenAuthenticated(function(oApi) {
  oApi.leaderboards.list(function(oResponse) {
    // Do something with the response.
  });
  oApi.quests.list(function(oResponse) {
    // Do something with the response.
  });
}

Or dependant requests can be chained on each other:

GoogleGamesApi.runWhenAuthenticated(function(oApi) {
  var oAchievementInstanceMap = {};
  var oPlayer;
  oApi.players.get(function(oResponse) {
    oPlayer = oResponse;
  }, {playerId: 'me'})
      .achievements.definitions(function(oResponse) {
        _.each(oResponse.items, function(oAchievementDefinition) {
          oAchievementInstanceMap[oAchievementDefinition.id] =
              oAchievementDefinition;
        });
      }).achievements.instances(function(oResponse) {
        _.each(oResponse.items, function(oAchievementInstance) {
          var oAchievementDefinition = oAchievementInstanceMap[oAchievementInstance.id];
          console.log('You have ' + oAchievementInstance.achievementState
              + ' the achievement ' + oAchievementDefinition.name);
        });
      }, {playerId: 'me'});
});

How it works…

The API is still a little rough, but I am pretty happy with how much easier it is to use than the original. The only part of the API that I am not happy with is integrating the authentication callback function with Google. Since the Google API requires the auth callback be attached directly to window and not another object on window (you cannot use GoogleGamesApi.authCallback directly). Consequently, GoogleGamesApi.authCallback must be set to a globally available variable and that global variable set to the google-signin-callback meta.

When the user authenticates, Google will automatically call GoogleGamesApi.authCallback and it will handle authentication. Calling any of the APIs when not authenticated will throw an GoogleGamesApi.ApiUnauthenticatedException error. The GoogleGamesApi.runWhenAuthenticated should be passed a callback function, which will execute as soon as the API authenticates or immediately when already authenticated. The callback will be passed a single argument, the GoogleGamesApi object for ease of use, instead of forcing the developer to call the global variable (GoogleGamesApi).

Inside the GoogleGamesApi.runWhenAuthenticated callback all API calls are available on oApi or GoogleGamesApi. These are the same object, so they can be used interchangeable, but be consistent. Calls to the API simply execute the underlying Google Play Games API. At the time of this writing, the first argument of all methods is the callback function (to pass the response into) and the second argument is an optional object for passing required/optional arguments to the Games API (these are defined on the API website[3]). I point this out, because while this makes most sense now, I may change the API signature if it something else makes more sense after more adoption, so check the github documentation. If a required parameter is not included, the underlying Games APIs will throw an exception.

Using the GoogleGamesApi object will make all API calls asynchronous, each returning as soon as the data is available. However, each API call also returns a new instance of itself, which can be chained (as shown in the last example). When chaining methods they use a promise-like system, waiting for the call to complete (and the callback executed), before running the next API call. This is useful when you have data in a dependant API call that references something from another API call (usually object definitions and instances, or a player object). This is one of the main failings of the Games API.

There’s more…

This is just the groundwork for building an Android inspired web UI. There is a lot to build still, and improvements to be added to the API layer. Some things that I expect to add soon are better authentication error handling (at least documenting a good auth recovery pattern), additions to the promise layer (such as a fail function), and possibly better caching. For the latter, the API currently leverages the browser URL caching, but there is probably room for an in memory map or a local storage technology. Anyway, there is loads more to come, but I wanted to start the project and share it with the community.

References

  1. Snake V3 w/ Google Games API
  2. Google Play Games Web UI Project
  3. Google Play Games API