gamefabric

GameFabric Integration

This document covers the GameFabricGameServerProviderPlugin, which integrates Pragma Engine with GameFabric for multiplayer game server allocation. It explains configuration, region selection, the allocation flow, GameFabric console setup, and how to run everything locally using ngrok.


Table of Contents

  1. Overview
  2. Architecture
  3. Configuration Reference
  4. Region Selection and the Game Instance Plugin
  5. Allocation Flow
  6. Ping Servers and Latency
  7. GameFabric Console Setup
  8. Example Unreal Game Server
  9. Running Locally with ngrok

Overview

The GameFabricGameServerProviderPlugin implements Pragma's GameServerProviderPlugin interface. When a game instance needs a dedicated server, this plugin sends an allocation request to GameFabric's REST API. GameFabric then finds an available game server in the requested region. The allocated game server connects back to Pragma to complete the handshake.

Key Files

File Purpose
GameFabricGameServerProviderPlugin.kt Plugin entry point, handles allocation requests with primary/fallback region logic
GameFabricApi.kt HTTP client for GameFabric's allocation and ping REST endpoints
GameFabricRegionPingCache.kt Caches ping server addresses so clients can measure latency to each region. Optional usage in your PartyPlugin
gameFabricGameInstance.proto Protobuf definitions for ping data and allocation error payloads
gameInstanceExt.proto Defines ExtAllocateGameServer with desired_region, game_server_version, and server_args

Configuration Reference

The plugin is configured under GameInstanceService.gameServerProviderPlugin in your YAML config:

game:
  pluginConfigs:
    GameInstanceService.gameServerProviderPlugin:
      class: "pragma.gameinstance.gamefabric.GameFabricGameServerProviderPlugin"
      config:
        gameFabric:
          gameFabricUrl: "https://STUDIO-IDENTIFIER.gamefabric.dev"
          allocatePath: "allocator/prod/us/allocate"
          pingPath: "ping"
          allocatorToken: "<your-bearer-token>"
          gameServerZonesByProviderRegion:
            us-west: "US_WEST"
            eu-central-1: "EU_CENTRAL"
        pragmaGamePartnerBackendAddress: "http://localhost:10100"
        fallbackRegion: "us-west"
        fallbackEnvironment: "prag"
        fallbackArmadaSet: "sample-game-armada-set"

Config Fields

Field Description
gameFabric.gameFabricUrl Base URL for your GameFabric studio (e.g. https://egret.gamefabric.dev)
gameFabric.allocatePath REST path appended to the base URL for allocation requests
gameFabric.pingPath REST path for fetching ping server addresses
gameFabric.allocatorToken Bearer token for authenticating with the GameFabric allocator API
gameFabric.gameServerZonesByProviderRegion Optional. Maps GameFabric region names (e.g. us-west) to Pragma GameServerZone values (e.g. US_WEST). Only regions present in this map are exposed to clients for ping
pragmaGamePartnerBackendAddress The address the allocated game server uses to connect back to Pragma's Game Partner Gateway
fallbackRegion If the primary region allocation fails, retry in this region. Leave blank to disable fallback
fallbackEnvironment Used when ExtAllocateGameServer.game_server_version is empty. Determines which GameFabric environment to target
fallbackArmadaSet Used when the armada set cannot be parsed from game_server_version

Game Server Version Format

The game_server_version field in ExtAllocateGameServer uses the format:

<environment>::<armadaSet>

For example: prag::sample-game-armada-set

Note that the two attributes env and armadaset will need to be configured on your GameFabric AramdaSet as labels in the General Settings area:

Label key Label value
allocator.nitrado.net/armadaset sample-game-armada-set
allocator.nitrado.net/env prag

Region Selection and the Game Instance Plugin

The desired_region used for allocation comes from the ExtAllocateGameServer protobuf message, which is populated in your custom GameInstancePlugin implementation.

How it works

  1. The GameInstancePlugin interface has methods like handleBackendCreateRequest and handlePlayerCreateRequest that are called when a game instance is being created.
  2. Inside these methods, you call gameInstanceSnapshot.allocateGameServer(ext) passing an ExtAllocateGameServer with your desired allocation parameters.
  3. The GameFabricGameServerProviderPlugin receives this ext in startAllocationForGameInstance and reads ext.desiredRegion as the primary region.

Default behavior (no custom plugin)

The default GameInstancePlugin.handleBackendCreateRequest implementation calls:

gameInstanceSnapshot.allocateGameServer(ExtAllocateGameServer.getDefaultInstance())

This means desired_region will be an empty string. In this case, the GameFabricGameServerProviderPlugin will directly use fallbackRegion as the primary allocation region.

If you do not provide a region through a custom GameInstancePlugin, you must configure fallbackRegion in the plugin config, otherwise allocation will fail.

Setting the region in a custom GameInstancePlugin

To properly set the region, override the relevant handler in your GameInstancePlugin and populate the ext:

override suspend fun handleBackendCreateRequest(
    gameInstanceSnapshot: GameInstance.GameInstance,
    requestExt: ExtBackendCreateRequest,
    playersToAdd: List<PlayerToAdd>,
) {
    playersToAdd.forEach { player ->
        gameInstanceSnapshot.addPlayer(player.playerId, player.partyId, player.teamNumber)
    }

    val ext = ExtAllocateGameServer.newBuilder()
        .setDesiredRegion("us-west")                          // from player ping results or matchmaking data
        .setGameServerVersion("prag::sample-game-armada-set") // environment::armadaSet
        .build()

    gameInstanceSnapshot.allocateGameServer(ext)
}

Allocation Flow

When startAllocationForGameInstance is called:

  1. Parse identifiers from ExtAllocateGameServer:

  2. Build the allocation request:

  3. Send allocation to GameFabric (primary region):

  4. Fallback on failure:

  5. Game server connects back:


[OPTIONAL] Ping Servers and Latency

GameFabricRegionPingCache could be used (eg: in a PartyPlugin) to periodically fetche ping server addresses from GameFabric so that game clients can measure latency to each region and choose the best one.


GameFabric Console Setup

Environment Variables

This is an example of environment variables configured directly on the region, therefore being inherited by all armada sets. These are set in the GameFabric console and are used by the allocator sidecar running alongside your game server:

Type Variable Name Secret Value / Key
Pod Field ALLOC_PRIORITY -- metadata.regionTypePriority
Key-Value Pair ALLOC_REGION -- us-west
Key-Value Pair ALLOC_URL -- https://STUDIO_IDENTIFIER.gamefabric.dev/allocator/prod/us/servers
Key-Value Pair ALLOC_PAYLOAD_ANNOTATION -- payload-
Key-Value Pair ALLOC_PAYLOAD_FILE -- /app/shared/payload.json
Secret ALLOC_TOKEN allocator-prod-us registry_token

What each variable does:

See the [Automatically registering game servers](https://docs.gamefabric. com/multiplayer-servers/multiplayer-services/server-allocation/automatically-registering-game-servers) section for more details on these environment variables.

Console Configuration Steps

  1. Create an Environment in the GameFabric console (e.g. prag).
  2. Create an Armada Set within that environment (e.g. sample-game-armada-set).
  3. Create a Fleet within the armada set for each region you want to support.
  4. Configure the environment variables listed above on each fleet. The ALLOC_REGION value must differ per fleet (e.g. us-west, eu-central-1).
  5. Create the allocator secret (allocator-prod-us) containing the registry_token that matches the allocatorToken in your Pragma config.
  6. Deploy your game server container image to the fleet. The container must watch /app/shared/payload.json for changes in order to get details Pragma assigns the specific game instance the game server will be associated with.

[OPTIONAL] Mapping Regions

The gameServerZonesByProviderRegion config maps GameFabric region names to Pragma GameServerZone values. Each region in GameFabric that you want to allocate to must have an entry here:

gameServerZonesByProviderRegion:
  us-west: "US_WEST"
  eu-central-1: "EU_CENTRAL"

The keys (e.g. us-west) must match the ALLOC_REGION values set on GameFabric fleets, and the values (e.g. US_WEST) are the Pragma-side identifiers used in matchmaking and game instances.


Example Unreal Game Server

The unreal/SampleGame directory contains a working Unreal Engine 5 project that demonstrates a dedicated game server integrating with both Pragma and GameFabric. It includes a server-side game mode, a client-side player controller, a Dockerfile for containerizing the server, and a script to build and push the image to GameFabric's container registry.

Project Structure

Path Purpose
Source/SampleGame/SampleGameGameModeBase.cpp/.h Dedicated server game mode - connects to Pragma, handles allocation, links the game instance, manages player connections
Source/SampleGame/MyPlayerController.cpp/.h Client-side player controller - login, party, matchmaking, game instance UI
Source/SampleGameServer.Target.cs Server build target (TargetType.Server)
Source/SampleGame/SampleGame.Build.cs Module dependencies including PragmaSDK, Agones, OnlineSubsystemSteam
Dockerfile Containerizes the cooked Linux server build with the GameFabric game server wrapper
push-image.sh Helper script to build the Docker image and push it to the GameFabric container registry

How the Game Mode Works

SampleGameGameModeBase supports two operating modes, selected automatically at startup based on whether command-line parameters are present:

Local development mode - When -PragmaGameInstanceId= is passed on the command line (along with partner tokens and backend address), the server connects to Pragma immediately. This is useful for iterating locally without GameFabric infrastructure (LocalProcessGameServerProviderPlugin would need to be configured on your locally running pragma-engine).

GameFabric mode - When no PragmaGameInstanceId is on the command line, the server assumes it is running inside a GameFabric-managed container. It polls every 2 seconds for the allocation payload file at /app/shared/payload.json, which the allocation sidecar writes when the server receives an allocation. The payload contains:

Field Description
GameInstanceId The Pragma game instance this server is allocated to
PartnerGameToken Auth token for the Game Partner Backend
PartnerSocialToken Auth token for the Social Partner Backend
pragmaBackendAddress URL of the Pragma backend the server should connect to

Once the payload is read, the server connects to Pragma, calls Link() with the game instance ID, and then calls ConnectMorePlayers() to make the server available to clients. When the game ends (triggered by a client sending "IWON"), it calls EndGame() and shuts down via Agones.

Dockerfile

The Dockerfile packages the cooked Unreal dedicated server for deployment on GameFabric:

FROM ubuntu:22.04

# Runtime dependencies for the Unreal dedicated server
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        ca-certificates libssl3 libcurl4 libfontconfig1 \
        libfreetype6 libglu1-mesa libsm6 libxrandr2 \
        libxinerama1 libxcursor1 \
    && apt-get clean -y && rm -rf /var/lib/apt/lists/*

# GameFabric game server wrapper (gswrapper)
ARG GSW_VERSION=v2.6.0
ADD https://github.com/GameFabric/gswrapper/releases/download/${GSW_VERSION}/gsw_linux_x86_64 \
    /app/gsw
RUN chmod +x /app/gsw

# Copy the cooked LinuxServer build
COPY --chown=gameserver:gameserver Build/LinuxServer/ /app/gameserver/

# gswrapper is the entrypoint -- it manages Agones lifecycle
# and tails game logs for observability
ENTRYPOINT ["/app/gsw", \
    "--shutdown.ready=1h", \
    "--shutdown.allocated=24h", \
    "--tail-log.paths=/app/gameserver/SampleGame/Saved/Logs/SampleGame.log", \
    "--"]

# Template variables {{ .GameServerIP }} and {{ .GameServerPort }}
# are injected by gswrapper at runtime
CMD ["/app/gameserver/SampleGameServer.sh", \
    "-server", "-log", \
    "-Hostname={{ .GameServerIP }}", \
    "-ExternalPort={{ .GameServerPort }}"]

Key aspects:

Building and Pushing the Image

push-image.sh builds the Docker image and pushes it to GameFabric's container registry. Run it from the unreal/SampleGame directory (where the Dockerfile lives):

./push-image.sh -p <registry-password> -t <tag>
Flag Description
-p Service-account password for the GameFabric container registry
-t Tag for the image (e.g. v1.0.0, build-1234)

The script uses three environment variables with sensible defaults that you should override for your project:

Variable Default Description
GF_REGISTRY_URL registry.gamefabric.io/your-org Your GameFabric container registry URL
GF_BRANCH development The registry branch/namespace for the image
GF_USERNAME your-service-account Service-account username for registry authentication

Example with overrides (or replace the overrides in the script itself):

GF_REGISTRY_URL="registry.gamefabric.io/my-studio" \
GF_BRANCH="production" \
GF_USERNAME="deploy-bot" \
./push-image.sh -p "$REGISTRY_PASSWORD" -t v1.0.0

The script will:

  1. Log in to the GameFabric container registry
  2. Build the image for linux/amd64
  3. Tag it as <GF_REGISTRY_URL>/<GF_BRANCH>/samplegame-server:<tag>
  4. Push it to the registry

After pushing, configure your GameFabric ArmadaSet to use this image for fleet deployments.


[OPTIONAL] Running Locally with ngrok

Follow the article here to learn how to expose your locally running pragma-engine to the outside world using ngrok.