In part 1 of this post, I showed how to create a SQL database in Windows Azure, create a schema for adding leaderboard functionality to a game, create an Entity Framework model for the database, and then create and test a WCF Data Service on top of the model that provides a rich REST-style interaction model with great query support via OData. If you have not yet read part 1, you should do so before continuing.
And if you want to follow along, but don’t yet have a Windows Azure account, you you can sign up for a 90-day trial that will give you everything you need.
If you’d like some back story on the games I’m planning to use to demonstrate the leaderboard service, check out this post.
In this second part, I’ll show you:
How I deploy the OData service to Windows Azure
How I wire up my Windows 8 JavaScript games, Space Cadet and Catapult Wars, to the leaderboard service.
## Deploying the Service
Now, I could start integrating the service into our games using the local version of the service, but since the service is pretty simple and I’m confident that it’ll do what I want, let’s go ahead and publish the service to Windows Azure, and use the published URI for the client app.
If you’ve never published a cloud service to Windows Azure, or it’s been a while since you’ve used the Visual Studio tools for Azure, you’ll be pleasantly surprised by how easy it is. All that’s required is a one-time setup of the publishing credentials, which I’ll start by right-clicking my Cloud project and selecting Publish…
[<img data-recalc-dims="1" loading="lazy" decoding="async" title="AzurePublish1_thumb[2]_2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="AzurePublish1_thumb[2]_2" src="https://dhcontent.blob.core.windows.net/images/2015/06/AzurePublish1_thumb2_2_thumb.png?resize=640%2C434" width="640" height="434" />][1]
For the first-time I’m publishing, I need to map my project to the Azure subscription I want to publish to, and since I haven’t yet set up a subscription, I’ll click the “Sign in to download credentials link” to set that up first. That takes me to the following page on the Windows Azure site, which also prompts me to download a .publishsettings file containing a certificate that Visual Studio needs for the publishing process:
[<img data-recalc-dims="1" loading="lazy" decoding="async" title="AzurePublish2_thumb[2]_2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="AzurePublish2_thumb[2]_2" src="https://dhcontent.blob.core.windows.net/images/2015/06/AzurePublish2_thumb2_2_thumb.png?resize=603%2C480" width="603" height="480" />][2]
Once I’ve downloaded the file (making note of where I saved it), I need to go back to Visual Studio, and click the Import…button, then browse to the .publishsettings file I downloaded in the previous step, and click the Open button. This should populate the drop-down with the new subscription information, so I can click Next. Since my subscription does not yet contain any cloud services to publish to, I’m prompted for a service name and a location where I’d like my service published, which I’ve populated in the dialog below:
[<img data-recalc-dims="1" loading="lazy" decoding="async" title="AzurePublish3_thumb[2]_2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="AzurePublish3_thumb[2]_2" src="https://dhcontent.blob.core.windows.net/images/2015/06/AzurePublish3_thumb2_2_thumb.png?resize=640%2C294" width="640" height="294" />][3]
Once I click OK, I’m greeted with a page in the publishing wizard that allows me to specify the settings for my service, as shown below:
[<img data-recalc-dims="1" loading="lazy" decoding="async" title="AzurePublish4_thumb[2]_2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="AzurePublish4_thumb[2]_2" src="https://dhcontent.blob.core.windows.net/images/2015/06/AzurePublish4_thumb2_2_thumb.png?resize=640%2C434" width="640" height="434" />][4]
Note that if I already had a service in production, I could change the Environment to Staging so that I could test the service prior to promoting it to Production. I can also choose to enable Remote Desktop access for my roles, if I want to have more control over the service configuration, or if I need to install any additional components for my service. There are additional settings under the advanced tab, but these are beyond the scope of this post. For my purposes, the defaults will do fine, so now I can either click Next to see a summary of the publish setttings I’ve chosen (and, if desired, save these as a profile for future use, which will enable faster future deployments), or just click the Publish button to publish my service.
When I click the Publish button, Visual Studio builds my project, and begins the deployment process, providing the status in the Windows Azure Activity Log window, as shown below:
[<img data-recalc-dims="1" loading="lazy" decoding="async" title="AzurePublish5_thumb[2]_2" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="AzurePublish5_thumb[2]_2" src="https://dhcontent.blob.core.windows.net/images/2015/06/AzurePublish5_thumb2_2_thumb.png?resize=640%2C230" width="640" height="230" />][5]
Once the log reports Complete, the Website URL above will be populated with the URL for the web role, where the service lives, in the form of http://_servicename_.cloudapp.net/. I’ll click the link to test the service is active.
Unfortunately, the first time I tried this, I ran into the generic ASP.NET error screen (aka the yellow screen of death). If you run into this screen, you’ll need to disable custom errors in web.config, and deploy a version of your cloud service using debug mode to see the detailed error information. Turns out that in my case, I had a conflict between the default version number for a WCF Service that was configured in the markup of the .svc file, and the version being loaded from the bin directory. After a quick [bing][6] search, I found <a href="http://blogs.msdn.com/b/astoriateam/archive/2012/08/29/odata-101-bin-deploying-wcf-data-services.aspx" target="_blank">this blog post</a>, which discusses the process of bin deploying WCF Data Services, and provides a couple of different workarounds, one of which was simply removing the version information from the markup, which I did. Once I deployed the new version, my service was up and running just the same as it was when I ran it locally. Awesome!
## Wiring up the Leaderboard Client App
One goal I have for this series of blog posts, and the accompanying data, is to make the leaderboard code as minimally intrusive to the basic game logic as possible. To that end, I’m planning to define all of the leaderboard logic in a single JavaScript file, with the functionality exposed to the client game using the WinJS namespace functionality, as in the following code example:
<div class="csharpcode">
<pre class="alt"><span class="lnum"> 1: </span>WinJS.Namespace.define(<span class="str">"Leaderboard"</span>, {</pre>
2: setPlayerName: setPlayerName,
3: init: init,
4: addWin: incrementWins,
5: addLoss: incrementLosses,
6: addTie: incrementTies,
7: updateScore: updateHighScore,
8: getTopTenScores: getTopTenScores,
9: getTopTenWins: getTopTenWinLossTie,
10: leaderboardList: leaderboardList
11: });
This allows me to expose a consistent API for leaderboard functionality, accessible by using the namespace Leaderboard, followed by the desired API member. So to initialize the leaderboard, I would call Leaderboard.init(args). A big advantage here is that the game itself doesn’t have to know anything about the underlying implementation of the leaderboard logic, and it’s pretty straightforward for me to change the internal logic of each of the functions to use a different back-end service without changing the code in the game itself (which is precisely what I’ll be doing in future posts on using ASP.NET Web API and Windows Azure Mobile Services to provide similar back-end services).
Another neat thing is that once I’ve referenced the JavaScript file in the game’s main HTML file (for example, in Dave Isbitski’s Space Cadet, this would be default.html), Visual Studio will automatically provide IntelliSense statement completion for my API, including the arguments required by a given function, as shown below:
Additionally, the IntelliSense lookup displays a comment which appears on the line prior to the function for that API as the description for the function. Makes it very convenient to add some simple documentation for your API.
As you can see from the code listing above, the API supports two basic types of score tracking…high score and win/loss/tie record. For the latter, we have functions to add a win, loss, or tie to the existing record, and for high score, we have a function to update the player’s score (assuming it’s higher than the existing high score for that player). Then, since we might want to display the current leaderboard information, we have a couple of functions that return the list of high scores, or the Win/Loss/Tie records for the current game. Both of these functions update an internal variable named leaderboardList, which is exposed as an API member with the same name.
Initializing the Leaderboard
I’ll start by declaring most of my variables up-top. Since JavaScript uses something called hoisting, which treats variable declarations anywhere within a given scope as if they were declared at the top of the function…BUT if the line where the variable is declared also includes initialization, the initialization is not hoisted, which can result in some tricky situations to debug. It’s a good practice to declare all variables at the top of the function scope in which they’re used:
1: var leaderboardClient, xhrOptions, leaderboardList, scores, playerName,
2: gameName, currentPlayerScore, diag;
Next comes the init function, which the game provides with the game and player names, and which checks to ensure that at least one score record exists and if not, intializes the leaderboard with a blank score record for that player/game combo. Note that in the interest of simplicity,
I have not added any code to ensure that the game/player combo is unique across all possible clients of the leaderboard service.
1: // player and game are required for initialization
2: function init(player, game) {
3: returnnew WinJS.Promise(
4: function (completed, error, progress) {
5: if (player) {
6: playerName = player;
7: }
8: else {
9: error("Player Name is required for initialization");
10: }
11:
12: if (game) {
13: gameName = game;
14: }
15: else {
16: error("Game Name is required for initialization");
17: }
18:
19: // replace with the URI for your custom service
20: leaderboardClient = {
21: baseUri: "http://[YOUR SERVICE URI]/GameLeaderServiceOData.svc/Scores",
22: query: "?$filter=Game eq '" + gameName + "' and Player eq '" + playerName + "'"
23: };
24:
25: // get score record for the current game/player
I’m introducing a new concept in this section of code, namely a Promise, which is functionality provided by the WinJS library, based on the CommonJS Promises/A specification. In my code above, I declare a new WinJS.Promise object, which receives as its input parameter a function (in this case an anonymous function) with three arguments, completed, error, and progress. These are actually functions you can call from inside the function passed to the promise, to indicate status (completed for success, error for failure, and progress for, well, progress). So in lines 5-17, I check to make sure that the game code has passed a player name and game name to the init function, and if not, call the error function to have the promise return an error condition. We’ll see how the game code can make use of this shortly.
Next, I initialize the leaderboardClient object with the base URI for my service, as well as some basic query parameters to ensure that I get just the data for this game and player for now, and then initialize the xhrOptions object with the url for my service/query, and add the Accept HTTP header with a value of “application/json” to indicate to the OData service that I want to receive the response in JSON format.
Then, I call WinJS.xhr, passing in the xhrOptions object. WinJS.xhr is a wrapper around the more familiar xmlHttpRequest object supported by most browsers, which turns a basic asynchronous web request into a Promise. Thus, rather than having to write a separate callback function, I can simply append .done() to the WinJS.xhr call, and pass in a function to be executed when the request is complete. The first argument to .done is the function to call when the request completes successfully, and I can optionally pass in a second function (lines 59-61 above) to be called if there’s an error during the request, as well as an optional third function for progress (not shown in the above code).
In the code above, I actually have two xhr requests. The first retrieves any score records for the current game and playername, and if it finds one, calls the completed function. If there is no score record found, another xhr request is made, this time an HTTP post request to add a new blank score record for this game/player (lines 35-53).
Here’s all we need to do in the game code to initialize the leaderboard:
It should be noted that if none of my game code relies on the leaderboard initialization being complete, I could simply call Leaderboard.init, without appending the .done, and the code will still work just fine. But using the promise object’s .done also allows me to handle any errors that might be thrown by the init function. This is a great reason to use promises in your own code…while the main benefit is an easier way to manage asynchronous code, it also makes it simpler to communicate error information to a client of a given library you may have written.
Getting and Updating Scores
Next, I define a function that I can call to get the current score for the current game/player, so I have baseline data when I’m getting ready to update a score record…again, I’ll make use of WinJS.Promise to enable the function to be called asynchronously:
1: function getCurrentPlayerScore() {
2: returnnew WinJS.Promise(
3: function (completed, error, progress) {
4: // get score record for the current game/player
And yes, before you start sending me any nasty emails, some of the code in this function could be leveraged for the init function as well, to reduce repetition…I’ll leave that particular refactoring as an exercise for the reader.
Essentially, as in the init function, I make an xhr request to my leaderboard service, and check to see if a record is returned (which it should be, since we’ve hopefully already called init!), and if so, passes the first score record to the completed function, which allows the caller to retrieve this score object.
So, for example, when I want to update the score via the updateScore API I’ve exposed on my namespace, I define the underlying function like so:
1: function updateHighScore(newScore) {
2: var scorePromise = getCurrentPlayerScore();
3: scorePromise.done(function (currentScore) {
4: if (currentScore.Score1 <= newScore) {
5: currentScore.Score1 = newScore;
6: updateScoreRecord(currentScore);
7: }
8: });
9: }
In this function, I’m showing a slight variation on the promise pattern. This time, I declare a variable called scorePromise that is passed the result of calling getCurrentPlayerScore. In line 3, I then call .done, passing in the function I want called as usual. This code just makes what’s happening (getCurrentPlayerScore returns a Promise object) a little more explicit, and may aid readability of my code. In line 4 above, I check to see if the new score passed into the function is higher than the score returned by getCurrentPlayerScore via the promise object, and if so, call updateScoreRecord, passing it the currentScore object.
NOTE: In the code above, you might notice that I’m checking “Score1”, when you might expect from the schema I showed in part 1 of this post, which had a column named “Score,” that the property should be “Score”. Well, this happened because of the way that I named the table (“Scores”) and column (“Score”), and the fact that in the process of building the Entity Framework model, the default is to singularize the entity names, so the entity in the model representing the Scores table is called “Score”. Because you can’t have an entity with a property name that is the same as the entity name, Entity Framework helpfully renamed the property “Score1” and WCF Data Services provides the same property on the Score object.
15: showMessage("Leaderboard could not be updated.");
16: });
17: }
This function takes the score record passed in from updateHighScore, prepares it for submitting using JSON.stringify, and then make the result the data property of the xhrOptions object. I also pass in a Content-type header to specify that I’m sending JSON data, as well as the X-HTTP-Method header with the value of “MERGE”. This tells the OData service to update the specified record with any properties provided, while leaving any unspecified values unchanged. You can read more about updating records via OData here.
Updating the high score from the game code is fairly straightforward, requiring only passing in the score to update:
1: Leaderboard.updateScore(score);
That’s all that’s needed to wire up the functionality to store and update score records for a given game (in this case, Space Cadet).
For Space Cadet, and similar games that allow the player to change their player name, I also added a function to update the player name, and there are functions to increment the Wins, Losses, and Ties properties for games that keep score using those values instead of a numeric high score. And for Catapult Wars, I can use the exact same JavaScript library. The only change needed is to call Leaderboard.addWin or Leaderboard.addLoss, depending on whether the player wins or loses the game.
Adding the Leaderboard Page
Of course, having all this leaderboard info doesn’t do me a lot of good if I don’t show it off every now and then, so to remedy that, I added a folder to my project called Leaderboard, and added to it a new Page control (which adds a matched set of HTML, CSS, and JavaScript files) called Leaderboard.html. Here’s what the markup looks like for Space Cadet:
It simply sets up a WinJS.UI.ListView control that I’ll use for databinding the score results from the service, with a template that displays each player name along with their score. There’s also a back button to return the player to the game once they’re done looking at the leaderboard.
Here’s the accompanying JS code:
1: // For an introduction to the Page Control template, see the following documentation:
Note that for Space Cadet, I’m leveraging the roaming settings support in Windows 8, which means that wherever I have the game installed, my player name will follow me, as long as I’m logged in with the same Microsoft Account. In the ready function, I first bind the click event of the back button to the goBack function, which simply reloads default.html. Next, I call Leaderboard.init (to ensure the leaderboard has been properly initialized for the game), and then call Leaderboard.getTopTenScores, and provide a function to be called when this operation is complete. In that function, I set the title of the leaderboard page to the name of the game plus “Top 10”, and finally set the itemDataSource of the ListView control to the dataSource property of the WinJS.Binding.List passed in to the function from getTopTenScores. Add a little CSS for formatting, and here’s the result:
Nothing fancy, I’ll admit, but it gets the job done. Here’s the code for the getTopTenScores function:
18: leaderboardList = new WinJS.Binding.List(scores.value);
19: completed(leaderboardList);
20: }
21: },
22: function (e) {
23: showMessage(e);
24: });
25: });
26: }
A couple of things to note are the use of the $orderby and $top query parameters, which help organize the data in the way I need it for the leaderboard (you might also notice that I don’t have a full 10 records in my database yet, but if I had more than 10 records, this query would only return the top 10), and that I’m not using the player name in the query this time, since I actually want to get scores for all players for this game.
Again, I’m returning a WinJS.Promise object, and if the xhr operation is successful, I create a new WinJS.Binding.List object based on the scores retrieved, and pass it to the completed function, which allows the Leaderboard.html page to bind it to the ListView control on the page. The leaderboard page for Catapult Wars looks almost identical, but in this case, I’m calling Leaderboard.getTopTenWins, and binding the Wins property in the ListView:
Wrap-up
So in this pair of posts, you’ve seen how I created a new SQL database in Windows Azure, defined a schema for the data, used Entity Framework to create a model of the data, and wrapped that in a WCF Data Service, providing a powerful, but simple URL-based query syntax that I can use to interact with my service. You’ve also seen how I created a single JavaScript library that used the WinJS namespace functionality to expose a simple API that can be used by multiple games to interact with the leaderboard service.
As noted in the overview post for this series, there are both advantages and disadvantages to this approach to building back-end services:
Advantages
Mature platform – SQL Server, Entity Framework, and WCF Data Services have each been around for numerous versions, and have had time to improve and grow over that time. Maturity also means greater familiarity for .NET developers.
Data format options – can return data in XML (ATOM) or JSON format.
Customizable – implementing custom logic can be done in a variety of ways, including attributes, custom code in the WCF data service, and exposing stored procedures as Service Operations.
Query flexibility – thanks to OData’s powerful query syntax, requesting and submitting data is easily accomplished by building the appropriate URL and making simple HTTP requests.
Disadvantages
Complexity – as you might have noticed from the length of this blog post, there are quite a few steps to getting this set up, and a number of layers. While this means more points at which the service may be customized, it also increases the overall complexity of the solution.
No client library for JavaScript apps – while there is direct support in XAML-based Windows Store apps for working with OData services, there is no official support for OData services in JavaScript apps. As you have seen, it’s pretty easy to build xhr requests that access the service, but this approach may seem rather foreign to anyone who’s used an OData client library, many of which use LINQ-style syntax for performing operations on the service, treating the service more like a local object, and which hide the underlying HTTP complexities from the developer.
So is this the right approach for your app? The answer, of course, is “it depends.” You can certainly build a robust, scalable, and easy-to-use service back-end for your Windows Store app or game using the techniques I’ve illustrated here. But there are other, simpler approaches that you may want to consider as well.
What’s Next?
In the next part of this series, I’ll show another approach to building back-end services, as I take on building this same game leaderboard service using the new ASP.NET Web API.
While you’re waiting, consider signing up for Generation App. There are lots of great resources available for building Windows 8 apps (and now for Windows Phone 8 as well). It’s free, and you control how often updates are sent, so there’s no good reason to pass it up. Sign up now!