Quest for serverless WebSockets, an Azure Functions adventure

  • How to use serverless WebSockets in C# Azure Functions to publish messages.
  • How to use serverless WebSockets in a JavaScript front-end to receive messages.
  • How to persist state in Azure Functions using Durable Entities.
Serverless WebSockets game play

Distributing state in serverless applications is complex

  • Developers should be up & running quickly to produce a prototype.
  • Operational maintenance should be minimal.
  • Game logic should be running in the cloud and exposed via an HTTP API.
  • A small amount of game data should be persisted.
  • Game data provided by the backend should be distributed in realtime across all players.
  • The client devices should cope with temporary connection issues and should receive messages in order.

The tech stack

  • Azure Functions, a serverless compute offering in Azure. This is used to create the HTTP API that clients can interact with.
  • Entity Functions, an extension of Azure Functions, used to persist small pieces of game & player state.
  • Ably, an edge messaging solution, that offers serverless WebSockets to distribute data in realtime.
  • VueJS, a well-known front-end framework.
  • Azure Static Web Apps, a hosting solution that serves the static files.
Diagram 1: Communication between player devices and the serverless application.
Diagram 1: Communication between player devices and the serverless application.

The game API

  • CreateQuest; triggered by the first player to start a new quest.
  • GetQuestExists; triggered by players who would like to join a quest to determine if they provided a valid quest ID.
  • AddPlayer; triggered by the player once they have selected their character and name.
  • ExecuteTurn; triggered by the player when they want to attack.
  • CreateTokenRequest; provides an authentication token and is triggered when a connection to Ably is made via the front-end.
Diagram 2: Communication flow within the HTTP Azure Functions.
Diagram 2: Communication flow within the HTTP Azure Functions.

Creating a new quest

CreateQuest function

GameEngine — CreateQuestAsync

  • Creating the monster, a Player (a non-player character, NPC).
  • Creating the initial GameState that contains the quest ID and the name of the game phase.
  • Adding the monster to the list of players in the GameState.

Stateful Entity Functions

  • Signaling (SignalEntityAsync), which involves one-way (fire and forget) communication.
  • Reading the state (ReadEntityStateAsync), which involves two-way communication.

Player entity function

GameState entity function

Publishing messages

Receiving messages client side

Running locally

  • .NET 6. The .NET runtime required for the C# Azure Functions.
  • Node 16. The JavaScript runtime required for the Vue front-end.
  • Azure Functions Core Tools. This is part of the Azure Functions extensions for VSCode that should be recommended for installation when this repo is opened in VSCode.
  • Azurite. This is a local storage emulator that is required for Entity Functions. When this repo is opened in VSCode, a message will appear to install this extension.
  • Azure Static Web Apps CLI. This is the command line interface to develop and deploy Azure Static Web Apps. Install this tool globally by running this command in the terminal: npm install -g @azure/static-web-apps-cli.
  • Sign up for a free Ably Account, create a new app, and copy the API key.

Steps

  1. Clone or fork the Serverless WebSockets Quest GitHub repo.
  2. Run npm install in the root folder.
  3. Rename the api\local.settings.json.example file to api\local.settings.json.
  4. Copy/paste the Ably API key in the ABLY_APIKEY field in the local.settings.json file.
  5. Start Azurite (VSCode: CTRL+SHIFT+P -> Azurite: Start)
  6. Run swa start in the root folder.
Function app contains non-HTTP triggered functions. Azure Static Web Apps managed functions only support HTTP functions. To use this function app with Static Web Apps, see 'Bring your own function app'.
Azure Static Web Apps emulator started at http://localhost:4280. Press CTRL+C to exit.

Deploying to the cloud

  1. Deploy the API separately to a dedicated Function App.
  2. Use the Standard (non-free) tier of Azure Static Web Apps.
  3. Update the configuration of SWA to indicate that a dedicated Function App is being used.

Deployment steps

  1. I’ve created a GitHub workflow to:
  • Create the Azure resources.
  • Build the C# API.
  • Deploy the API to the Function App.
    This approach uses the Azure Login action and requires the creation of an Azure Service Principal, as is explained in more detail in this README.

Summary

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store