Azure Service Bus: Connecting Unity3D

Overview

This TechNet article provides an illustration of connecting a Unity3d game to a Microsoft Azure service bus.  This does turn out to be relatively simple but there are several issues that might arise and need to be overcome. 

Unity3D

Unity is a game development platform that can be used to develop both 3D and 2D games and supports building for a large variety of platforms including Android, Windows, Xbox, iOS and other platforms.  Unity supports javascript and C# as development languages but an important fact to emphasize is the engine uses the C# compiler Mono to build the game.

Because Unity uses the Mono compiler, this means the version of our assemblies is limited to .NET 3.5 or lower as shown when the Microsoft ServiceBus library is directly referenced:

Note, it is possible to include more recent .NET assemblies using the experimental setting.  This can be found in PlayerSettings under Configuration:

Mono 

Mono is an open source development platform used to build cross-platform applications.  Supported by Microsoft and the .NET Foundation, the Mono project has an active and enthusiastic contributing community. 

Azure

This article will not cover the creation of an Azure Service Bus as this is covered in many articles and in the Microsoft Documentation. The illustration will send a message to an already created queue using a Shared access policy.  Because of this, a policy with Send permission needs to be created as illustrated below:

Integration

There are two main ways of connecting to Azure from Unity: managed assemblies and REST API.  The author has had limited success and a large amount of frustrating evening trying to use managed assemblies.  Even using the very promising open source solution on GitHub and based on posts on stack overflow and the GitHub project, others have also faced similar challenges.   Because of this, the Azure Service Bus REST API will be used to submit messages to the Azure Service Bus.

  

Scenario

This illustration will be only to send a message to an Azure Service Bus Queue.  To make the article more interesting, the target platform will be Android.  This means that .net assemblies cannot be directly used in the solution as these cannot be built into the solution.  If this is attempted, a similar error to the below example will be encountered:

Initial Setup

Starting with a newly created unity project, a new game object called is created: AzureMessageSender. 

On the new object, a new script is created called... how about MessageSender

This script will be used to manage the communication between the game and the Azure Service Bus queue.  In order to not impact the main thread's performance and cause a significant dip in the refresh rate of the game, all communication to Azure will be done in coroutines.  Launching the coroutines will be performed by a button press.  The following shows the method that the button press will trigger:

A new button is then added to the scene:

And the SendMessage() method is associated with the click event:

Sending Message to Azure

A challenging aspect of communicating to Azure via the REST API is generating the Shared Access Signatures (SAS).  This is well documented but does not go into enough detail to generate compliant signatures without using the REST API manage libraries.  As this project will not build with a .NET assembly referenced, there needs to be an alternative.  Fortunately, the GitHub .NET Standard client library for Azure Service Bus is open source and available.  

Adding the SharedAccessSignatureTokenProvider 

As a first attempt to leveraging all the hard work the community has done, the entire project is dropped directly into a Unity assets subfolder:

 Unfortunately, Unity is not able to build the newer C# syntax that was used in the project:

Fortunately, if just the SharedAccessSignatureTokenProvider and the necessary referenced classes are added to the project, Unity is able to build the class without an issue.  This is a large class and the details are out of the scope of this article so please see the project link below for a working copy.

Sending a message

Sending a message will be done using a WebClient asynchronously.  Simply put, we will send the message and not block the thread but instead listen for when the response is available.  The following shows the co-routine:

01.IEnumerator SubmitMessageToAzure()
02.{
03.    yield return  new WaitForEndOfFrame();
04. 
05.    // first get an access token if one is missing
06.    if (_sasProvider == null)
07.    {
08.        _sasProvider = new  SharedAccessSignatureTokenProvider("IGLGuestClient", // issuername
09.                                                                "...", // issuersecret
10.                                                                new TimeSpan(1, 0, 0));
11.    }
12. 
13.    var content = Encoding.Default.GetBytes("This is my message!");
14. 
15.    using (WebClient webClient = new WebClient())
16.    {
17.        var token = _sasProvider.GetToken("http://indiegameslab.servicebus.windows.net/", "POST",  new  TimeSpan(1, 0, 0)); // realm
18.        webClient.Headers[HttpRequestHeader.Authorization] = token.TokenValue;
19. 
20.        // add the properties
21.        var collection = new  NameValueCollection();
22.        collection.Add("MyCustomHeader", "SomeCustomValuelikemaybetheplayerid");
23.        webClient.Headers.Add(collection);
24. 
25.        webClient.UploadDataCompleted += WebClient_UploadDataCompleted;
26.        webClient.UploadDataAsync(new Uri(string.Format("https://{0}.{1}/{2}/messages", "indiegameslab", "servicebus.windows.net", "echo")),  "POST", content);
27.    }
28.}

The token provider is instantiated only once (line 8) but the token is generated each request (line 17).  This is because the token contains the current time and will be rejected if the request is considered old.

It is interesting to note that the content is bytes so can be a string, image, file, etc. (line 13) and custom header information can be sent (line 23).  Also worth emphasizing is the method is designed to impact the performance as little as possible.  The impact of a send can be measured in the profiler and is illustrated below:

A successful callback indicates the message was submitted to the queue.  This does not have content but an exception will be thrown if the result is referenced as shown in the callback method below:

01.private static  void WebClient_UploadDataCompleted(object sender, UploadDataCompletedEventArgs e)
02.{
03.    try
04.    {
05.        string responseString = Encoding.UTF8.GetString(e.Result);
06. 
07.        if(string.IsNullOrEmpty(responseString))
08.        {
09.            Debug.LogFormat("Successfully submitted to the Service Bus.  Check the Queue!");
10.        }
11.        else
12.        {
13.            Debug.LogWarningFormat("Odd.  We received a polite message back from Azure:{0}", responseString);
14.        }
15.             
16.    }
17.    catch (Exception ex)
18.    {
19.        Debug.LogErrorFormat("Failed to submit a message to Azure:{0}", ex.Message);
20.    }
21.}

Summary

Like many programming tasks, the end solution is simple but it may take a considerable amount of trial and error to come up with a solution that works. This article shows one approach to sending a message to the Azure Service Bus.  Though it is possible to use managed .NET libraries for submitting to Azure, in some applications this is not possible due to .NET assembly version constraints or because the target platform is not .NET but JavaScript.  

The aim of the article is to save someone frustration.  If it has, please comment!

You can find a working sample of both sending and receiving messages from the Azure Service Bus in the GitHub project IndieGamesLab-Samples.