Microsoft Game Dev Blog

Twitter icon

Reacting to PlayFab PlayStream Events with Azure Functions

PF-liveops.jpg

Introduction

As my teammate Andreas Pohl wrote in his article Build vs. Buy - Which online service is right for my game, PlayFab is a great way to get started with a backend for your game.

Now you started with PlayFab and you have come to the point where you want to extend the functionality with your own game logic, for example by leveraging Azure PlayFab's PlayStream and reacting to it's events. How would you do that?

Good news: PlayFab has an integration with Azure Functions! In this article, I will show you how you can react to PlayStream Events with Azure Functions and Azure Storage.

Discourse: What are Azure Functions?

From the official documentation:

Azure Functions allows you to run small pieces of code (called "functions") without worrying about application infrastructure. With Azure Functions, the cloud infrastructure provides all the up-to-date servers you need to keep your application running at scale.

A function is "triggered" by a specific type of event. Supported triggers include responding to changes in data, responding to messages, running on a schedule, or as the result of an HTTP request.

While you can always code directly against a myriad of services, integrating with other services is streamlined by using bindings. Bindings give you declarative access to a wide variety of Azure and third-party services.

Goal

This is a detailed end-to-end example on how to extend Azure PlayFab with Azure Functions.

We will be using C# & .NET Core to create an Azure Function and bind it to a PlayStream Event. The event to bind to will be the player_created Event, which fires every time a new player registers. The binding will then call the Azure Function.

This Azure Function will add "User Data" to this new Player.

Title User Data is title-specific custom data for the user which is readable and writable by the client. But you can of course set this via the Server as well. Player Data and User Data are terms used interchangeable within PlayFab terminology. Please refer to the documentation for more information.

Prerequisites

Editor/IDE

While you can follow along with basically any Editor/IDE, I will be using Visual Studio 2019 (you may use the the free Visual Studio 2019 Community Edition!) because it has all the batteries included for our tasks ahead.

If you do not use Visual Studio, but want to use another IDE/Toolset, there are examples for creating & publishing Azure Function Apps with Visual Studio Code or via CLI.

Azure Access

If you do not yet have used Azure, you sign up for free and get some additional goodies with Visual Studio Dev Essentials program.

Azure PlayFab access

And, of course, since this is a Tutorial on how to use Azure PlayFab, you need an existing Title on PlayFab as well. You could use the same Microsoft Account you used for signing up to Azure. Remember: There is a free tier to get you started!

Getting Started

As there are so many languages and platforms supported by Azure PlayFab and Azure Functions, I had to decide for one and chose the language and platform I am best in: Pure C# & .NET Core. But there are great guides for various Game Engines like Unity and Unreal, and PlayFab provides SDKs for a plethora of languages that are very similar to the C# SDK because they are all auto-generated from the same REST API.

So while this is C#, most of what we do here is very similar in other languages/runtimes

Project Setup

Create an Azure Functions Project

To create an Azure Functions Project, open up Visual Studio and either use the splash screen (see screenshot below) or use File --> New Project…

pfevents_1.jpg

Select the project type "Azure Functions" and click "Next"

pfevents_2.jpg

Choose a name for your Project and select the folder for it to be placed in:

pfevents_3.jpg

This will create a folder named according to your Solution name, in which it will create another folder for the project, with the Project's name you chose.

Now, in the next dialog, you may either choose an HttpTrigger or a QueueTrigger for your Function. Please choose QueueTrigger.

Please be aware that Azure PlayFab terminates PlayStream-invoked Azure Functions after 1 second, thus QueueTrigger is the better choice here since it will immediately yield back to Azure PlayFab.

For the Storage Account drop-down, you may use Storage Emulator (which comes with Visual Studio or you can use the new "Azurite", which is the new generation of the Storage Emulator) or click "Browse…" to select a Storage Account. As we will need to test it with Azure PlayFab anyways, let's go ahead and use an Azure Storage Account. Please click "Browse…" to create a new Storage Account:

pfevents_4.jpg

In the next dialog, click "Create a storage account"

pfevents_5.jpg

When creating the storage account, make sure to create a unique name. It must be globally unique, because it will be part of the URI. You will see a warning if it is not unique.

Also, you should create a new resource group for this project. A resource group is basically the topmost container in Azure and can contain all the different resources we are going to create. For example, if you want to remove all the things we are creating in this article, you could just remove the resource group. You can read more about the Resource Manager deployment model and resource groups in the documentation.

Last, let's think about the Account type. While it is enough for the sake of this tutorial to choose "LRS - Locally Redundant Storage", it it is worth considering other redundancy options for different use-cases.

Click "Create" when you have filled all fields and created a resource group.

pfevents_6.jpg

Now back to the Storage Account select screen, select the one you just created and click "Add".

pfevents_7.jpg

The Storage Account should now appear in the drop-down, beneath "Storage Emulator" and "Browse...". Select it.

I would advise to leave "Connection string setting name" empty for now. Using this would create a new setting and would be a bit confusing for now.

For "Queue name", make up a name for the storage queue (which we will actually create later). It does not have to have a unique name, but it may only use characters, numbers and dashes.

Just call it login-events for now.

pfevents_8.jpg

Once you are done setting up the Storage Account configuration, click the "Create" button and wait for the project to generate. Visual Studio will eventually open the project and show you the code of the generated Azure Function stub (your namespace name may vary):

pfeventscode_1.JPG

You might have noticed, that the Function was named Function1, because Visual Studio just does not know what we want to do. Please rename the File, Class and the Function name in the Annotation as PlayStreamEventHandler.

Renaming is done easiest in Visual Studio by right-clicking the Class name in the code editor and selecting "Rename" (or just press F2). This will open a tiny dialog asking you where you want to replace. Select "Rename file" and "Include strings". This will make sure Class and File are renamed as well as the Attribute string.

pfevents_9.jpg

While asking for a Connection string setting in the dialog, we did not give it a name. The reason is, that Visual Studio adds a connection string to the Storage Account we previously selected, by default It is called AzureWebJobsStorage. This is set in the local.settings.json config file:

pfevents_10.jpg

local.settings.json is a config file only for local configuration. For deployed functions, you can either use the Application Settings built into Azure Functions, or for more advanced scenarios, you could use Azure App Config.

But because we did not give the connection setting a name earlier, there was no name set in the function. If you look closely at the Azure Function code, you can spot it in the Signature of the Run method. Look at the Connection = "" property of the QueueTrigger attribute:

pfeventscode_2.JPG

We just need to put in AzureWebJobsStorage instead of the empty string to load the right connection string:

pfeventscode_3.JPG

Add the Azure PlayFab package

The Azure PlayFab SDK for C# is available via NuGet. You can either add the latest version via the dotnet CLI

dotnet add package PlayFabAllSDK
Or via the NuGet package manager in Visual Studio: Right-click "Dependencies" and click "Manage NuGetPackages"

pfevents_11.jpg

pfevents_12.jpg

Open the "Browse" tab and search for PlayFab. Select the PlayFabAllSDK and click "Install" on the details pane to install. You might need to accept licenses when you do.

Please note: you should also update the installed packages because the Visual Studio template ships with a slightly older version of the SDKs for Azure Functions and WebJobs Storage.

Create a Storage Queue

The last missing piece before we have all the infrastructure to test the Function the first time is a Storage Queue. Now let's create one!

Go to the Azure Portal and open up your Storage Account's Queue blade:

pfevents_13.jpg

Created a Queue by clicking the "+ Queue" button. Make sure to use the same name you specified when creating the Azure Function with the Queue Trigger (see screenshot below). We used to call it login-events´.

pfevents_14.jpg

Test the Function

Once you have created the Queue, click the Queue entry in the Portal to get to the detail Blade. Here, click "+ Add message". In the new Dialog, just add some text and click "OK".

pfevents_15.jpg

Back in Visual Studio, press F5 (or click Build --> Debug) to launch the local Azure Functions runtime in debug mode.

Now watch the console window of your Function: it will log out how it processes the queue message you just added!

pfevents_16.jpg

Checking back in the Azure Portal's view of the Queue, you click the "Refresh" button to see how the message disappeared, because it got processed.

If you want to inspect the Queue without actually de-queuing it, you can use the Azure Storage Explorer.

Set up PlayFab

To call your Azure Function from Azure PlayFab, we need to register the Function binding via the Azure Storage Queue and set up an Event Trigger (aka "Rule").

Register the Function

Log in to Azure PlayFab and click on the Title you want to register the Azure Function for.

Once you are in the Title's overview, click "Automation" (1), which opens the "Cloud Script" tab. In this tab, select the sub-tab "Functions (Preview)" (2), which will list all the previously registered Azure Functions. Now click the big orange button in the top right labeled "REGISTER FUNCTION" (3) to open the registration dialog.

pfevents_17.jpg

Let's go through the individual settings for a registration:

pfevents_18.jpg

  • Trigger type: set to "Queue"
  • Function name: This is not actually the name of the Azure Function you deployed, but the name of the binding we're currently setting up – so it's the identifier you will be working with from PlayFab. Choose a name that fits your needs!
  • Queue name: this is the queue name the QueueTrigger of the Azure Function listens to. In the examples above, this was login-events.
  • Connection string: The ConnectionString to the Azure Storage Account Queue.

    To finish registering the Function registration, click "REGISTER FUNCTION". The Registration should now appear in the overview.

    Set up a Rule

    To set up a Rule, within a PlayFab Title, go to "Automation" (1) in the left pane, select the "Rules" Tab (2) and click "NEW RULE" (3).

pfevents_19.jpg

A Rule is a configuration that listens on a PlayStream event emitted by Azure PlayFab. When such an event occurs, the Rule checks whether the configured Conditions are met and if they are, triggers the configured Action.

In our case, we want no conditions, but want to "Execute Azure Function". In fact, what it does, is calling its own internal binding we just set up and provides it the PlayStream event data and additional arguments you may specify here (in JSON format) as argument to that binding.

When that binding is called, it will either call the Function via HTTP trigger, or, as we configured it, by writing the data to the storage queue for the Azure Function to pick up via a QueueTrigger.

pfevents_20.jpg

Click "SAVE ACTION" to save (yes, this saves the Rule 😉). You should now see the Rule in the overview.

Trigger the PlayFab Rule

Now we want to test the Rule and trigger the Azure Function via an Azure PlayFab PlayStream Event. To do this, we need to raise an event of type com.playfab.player_logged_in, as we specified when we created the Rule.

To achieve this, let's create a small CLI application to log a Player in.

In Visual Studio, create a new .NET Core Application (preferably in the same Solution as the Function app) and then install the PlayFabAllSDK, just like above.

Now, you can paste the following C# code to your Program.cs to have a working program that signs into your game and if the Player does not yet exist, it will create a new one.

Well, at least it is almost working. You need to substitute XXXX with your own Title ID. You can get the Title ID from your Studio's Overview Page.

Below code listing as GitHub Gist.

pfeventscode_4.JPG

pfeventscode_5.JPG

pfeventscode_6.JPG

Now execute the program and watch how the event is executed in PlayFab via the PlayStream Monitor!

playeraction.jpg

Backend implementation

It's time to start the actual backend implementation!

Using the PlayFab Context

Before we even get started with writing backend code, you will need a Helper classes file, provided by PlayFab. Download the file and save it to your project (directly beneath the *.csproj or the Function class file). Here is why:

Every time a Function is triggered, The Context in which it was executed is sent as a parameter. The Context includes the following when the Function is triggered via a PlayStream Event:

  • The Entity Profile
  • The PlayStream event which triggered the script.
  • A boolean that indicates whether a PlayStream event is sent as part of the function being executed
  • The Payload data you specified when setting up the binding in PlayFab

    For more information on the Context and what it contains in other environments (like if an Azure Function is called from a Game Client), please see the Using CloudScript context models Tutorial.

    Parsing the Context

    We already receive the Context in the Function we have created, it is the myQueueItem parameter:

pfeventscode_7.JPG

However, it is just a JSON string, and we want it to be parsed to a C# POCO ("Plain Old C# Object").

To do this, we first need the Class to convert to. The easiest way to do this is to use the PlayFab helper classes we downloaded and added above. It will contain all the Context Objects you need for the various deserialization use-cases.

Now, we can actually deserialize the Context to PlayerPlayStreamFunctionExecutionContext:

pfeventscode_8.JPG

Authentication

Authentication in Azure PlayFab might seem a little bit overwhelming at first, but it enables high flexibility and best operational security by allowing you to only make requests in the relevant context.

For server-side actions, like we want to do here, this is particularly simple: we only need to set the TitleId and the DeveloperSecretKey on PlayFabSettings.staticSettings. Let's do that!

Retrieving the TitleId

This one is the simplest - you can just get it from the context we have deserialized:

pfeventscode_9.JPG

Retrieving the DeveloperSecretKey

The DeveloperSecretKey must be retrieved from the Azure PlayFab Portal.

Log in and go to your Title you are working on. Then, click the little cogwheel beneath your Title's name (1) and select "Title Settings" (2). Last, select the "Secret Keys" Tab (3)

pfevents_21.jpg

On This page, go ahead and click "NEW SECRET KEY". Specify a name and click "SAVE SECRET KEY".

Now back on the Secret Key Overview, copy the secret key.

Store the secret

Since we do not want to store the secret directly in the code, we will create a new setting PlayFabDeveloperSecretKey in local.settings.json. Substitute <YOUR SECRET KEY> with the secret you just copied from PlayFab:

pfeventscode_10.JPG

Authenticating using PlayFab Static Settings

Now that we have all the information, let's go ahead and put together the code.

Go to your Function's code, and paste the following code below your parsed context:

pfeventscode_11.JPG

The function should now look like this:

pfeventscode_12.JPG

This is already all we need to make requests using PlayFabServerAPI or PlayFabAdminAPI - now we only need to make a request!

Updating Player Data

Create a new Method UpdatePlayerData as below:

pfevents_22.JPG

an call it in your Function's Run() method:

pfevents_23.JPG

Your full function code should now look something like this:

pfevents_24.JPG

pfeventscode_13.JPG

pfeventscode_14.JPG

pfeventscode_15.JPG

Ok, that's quite a chunk. Now what does UpdatePlayerData() do? Let's analyze the different blocks:

Creating a Request object

pfeventscode_16.JPG

Each PlayFab API request has a corresponding Request object, which is named accordingly and is located in the Namespace PlayFab.<API TYPE>Models, where <API TYPE> corresponds to one of the many APIs, like the Server, Admin, User or Authentication APIs, among others.

The UpdateUserDataRequest requires a PlayFabId to know which Object to update. In our case, we want to update a Player, thus we need to pass a PlayerId, so it knows which Player to update. You can retrieve that from the Context that we deserialized earlier.

Now to the core: Adding a Dictionary<string, string> as Data, containing Key/Value pairs for the Data we want to set on the Player- e.g. the amount of Mana.

Sending the Request

pfeventscode_17.JPG

Sending the request is as simple as calling the static Method PlayFabServerAPI.UpdateUserDataAsync() and passing it the request object we created before as a parameter. This method will return a Result object, we can then check for errors.

Checking for errors

pfeventscode_18.JPG

Basically, whenever the Error property on the Request object is non-null, an Error has occurred. There are a couple more Properties on the Error object which help you identify the problem before going further.

Testing locally

Start the the Function locally, e.g. by pressing F5 in Visual Studio. Then, launch the game-cli to generate a new Player which then creates a PlayStream event, triggering the Rule which calls the Function. As we trigger the function using a Queue, we can again just read them locally.

Once the Function has processed the new trigger, go back to Azure PlayFab and see whether the newly created player is listed in Azure PlayFab's Player listing. Then go to the player's detail view and click "Player Data (Title)". You should now see the newly added key/value pair - in my case it's foo and bar:

pfevents_25.jpg

Deploying the Function

Now that we have a working Azure Function, let's deploy it to Azure!

If you do not use Visual Studio, but want to use another IDE/Toolset, there are examples for creating & publishing Azure Function Apps with Visual Studio Code or via CLI.

Deploy with the "Publish" command

To get started and for the sake of this demo, we will be using "right click deploy", the deployment mechanism integrated in Visual Studio. While this is convenient for a demo, please consider Azure DevOps or GitHub Actions to build your automated Continuous Integration & Continuous Deployment (CI/CD) pipelines for a real project – because manual deployments are error prone, require knowledge by a team member etc. Thus, there is the saying: "Friends don't let Friends right-click publish!"

In Visual Studio, right-click the project and click "Publish":

pfevents_26.jpg

This will open a dialog where you can configure to which Azure Function App you want to deploy – and you can even create a new one using this dialog!

While this deployment dialog also supports other deployment methods, please select "Azure".

pfevents_27.jpg

Then, "Azure Functions App (Windows)".

pfevents_28.jpg

In the following Dialog you can choose the Azure Function App or slot beneath an app to deploy to.

If you haven't already created an Azure Function App to deploy to, you can use the "Create a new Azure Function…" link to do so:

pfevents_29.jpg

pfevents_30.jpg

Now select the Function App you want to deploy to:

pfevents_31.png

Once selected, Visual Studio will pull the "Publishing Profile" from Azure.

WARNING Never add the user part of the publishing profile (*.pubxml.user) to source control, it contains your encrypted password!

Now click "Publish" to deploy your Azure Function App.

Testing the online Function

Since you have now deployed the Azure Function, let's go ahead and test it.

First, make sure no local instance of the Function is running, so it is not competing against the online Azure Function when reading from the Azure Storage Queue.

Next, launch the game-cli again, which should log-in a new Account and trigger the Function via the Storage Queue.

You should now see the entry we created in the PlayFab data.

Taking it further

Another possibility is to use Azure Functions to extend PlayFab even further, like enabling real-time messaging with Azure SignalR Service or a global database like CosmosDb!

Conclusion

In this article, we learned how to extend PlayFab by reacting to PlayFab PlayStream events with Azure Functions using Rules in PlayFab.

We created the binding, added a rule and set-up an Azure Function app with a Storage Queue trigger. The Azure Function reads a message from the Storage Queue, which contains the PlayFab context. The context is used to reference the registered Player when we update it's Player data on Playfab.

Last, we learned about the greater potential of extending Azure PlayFab with Azure Functions.

I hope you enjoyed this end-to-end example. Follow us on Twitter @MSFTGameStack and ask me about this post or all things Azure or have a look at my GitHub!

Please also find the full source for this example at my GitHub repository: https://github.com/Structed/sample-playfab-eventstream-handler


From the Microsoft Game Dev blog

Several blocky characters stand outside a school entrance in Minecraft

Creating for everyone: How to make more accessible games

Article // May 9, 2022

Sarah Bond shares at the Microsoft Ability Summit why accessibility in gaming is so critical

Forza Horizon 5 crosses the finish line, fueled by AKS

Article // May 2, 2022

FH5 relies on autoscaling Azure Kubernetes Service (AKS) clusters to meet the most challenging performance demands.

LanaLuxblogimg1.jpg

Lana Lux: Twitch Live-Coder and Creator of Strain

Article // Apr 29, 2022

Meet Lana Lux, a game developer, designer, Twitch streamer and invested in an accessible, supportive community