This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Code Blind Game Server Client SDKs

The SDKs are integration points for game servers with Code Blind itself.

Overview

The client SDKs are required for a game server to work with Code Blind.

The current supported SDKs are:

You can also find some externally supported SDKs in our Third Party Content.

The SDKs are relatively thin wrappers around gRPC generated clients, or an implementation of the REST API (exposed via grpc-gateway), where gRPC client generation and compilation isn’t well supported.

They connect to a small process that Code Blind coordinates to run alongside the Game Server in a Kubernetes Pod. This means that more languages can be supported in the future with minimal effort (but pull requests are welcome! 😊 ).

There is also local development tooling for working against the SDK locally, without having to spin up an entire Kubernetes infrastructure.

Connecting to the SDK Server

Starting with Code Blind 1.1.0, the port that the SDK Server listens on for incoming gRPC or HTTP requests is configurable. This provides flexibility in cases where the default port conflicts with a port that is needed by the game server.

Code Blind will automatically set the following environment variables on all game server containers:

  • AGONES_SDK_GRPC_PORT: The port where the gRPC server is listening (defaults to 9357)
  • AGONES_SDK_HTTP_PORT: The port where the grpc-gateway is listening (defaults to 9358)

The SDKs will automatically discover and connect to the gRPC port specified in the environment variable.

If your game server requires using a REST client, it is advised to use the port from the environment variable, otherwise your game server will not be able to contact the SDK server if it is configured to use a non-default port.

Function Reference

While each of the SDKs are canonical to their languages, they all have the following functions that implement the core responsibilities of the SDK.

For language specific documentation, have a look at the respective source (linked above), and the examples.

Calling any of state changing functions mentioned below does not guarantee that GameServer Custom Resource object would actually change its state right after the call. For instance, it could be moved to the Shutdown state elsewhere (for example, when a fleet scales down), which leads to no changes in GameServer object. You can verify the result of this call by waiting for the desired state in a callback to WatchGameServer() function.

Functions which changes GameServer state or settings are:

  1. Ready()
  2. Shutdown()
  3. SetLabel()
  4. SetAnnotation()
  5. Allocate()
  6. Reserve()
  7. Alpha().SetCapacity()
  8. Alpha().PlayerConnect()
  9. Alpha().PlayerDisconnect()
  10. Alpha().SetCounterCount()
  11. Alpha().IncrementCounter()
  12. Alpha().DecrementCounter()
  13. Alpha().SetCounterCapacity()
  14. Alpha().AppendListValue()
  15. Alpha().DeleteListValue()
  16. Alpha().SetListCapacity()

Lifecycle Management

Ready()

This tells Code Blind that the Game Server is ready to take player connections. Once a Game Server has specified that it is Ready, then the Kubernetes GameServer record will be moved to the Ready state, and the details for its public address and connection port will be populated.

While Code Blind prefers that Shutdown() is run once a game has completed to delete the GameServer instance, if you want or need to move an Allocated GameServer back to Ready to be reused, you can call this SDK method again to do this.

Health()

This sends a single ping to designate that the Game Server is alive and healthy. Failure to send pings within the configured thresholds will result in the GameServer being marked as Unhealthy.

See the gameserver.yaml for all health checking configurations.

Reserve(seconds)

With some matchmaking scenarios and systems it is important to be able to ensure that a GameServer is unable to be deleted, but doesn’t trigger a FleetAutoscaler scale up. This is where Reserve(seconds) is useful.

Reserve(seconds) will move the GameServer into the Reserved state for the specified number of seconds (0 is forever), and then it will be moved back to Ready state. While in Reserved state, the GameServer will not be deleted on scale down or Fleet update, and also it could not be Allocated using GameServerAllocation.

This is often used when a game server process must register itself with an external system, such as a matchmaker, that requires it to designate itself as available for a game session for a certain period. Once a game session has started, it should call SDK.Allocate() to designate that players are currently active on it.

Calling other state changing SDK commands such as Ready or Allocate will turn off the timer to reset the GameServer back to the Ready state or to promote it to an Allocated state accordingly.

Allocate()

With some matchmakers and game matching strategies, it can be important for game servers to mark themselves as Allocated. For those scenarios, this SDK functionality exists.

There is a chance that GameServer does not actually become Allocated after this call. Please refer to the general note in Function Reference above.

The agones.dev/last-allocated annotation will be set on the GameServer to an RFC3339 formatted timestamp of the time of allocation, even if the GameServer was already in an Allocated state.

Note that if using SDK.Allocate() in combination with GameServerAllocations, it’s possible for the agones.dev/last-allocated timestamp to move backwards if clocks are not synchronized between the Code Blind controller and the GameServer pod.

Shutdown()

This tells Code Blind to shut down the currently running game server. The GameServer state will be set Shutdown and the backing Pod will be Terminated.

It’s worth reading the Termination of Pods Kubernetes documentation, to understand the termination process, and the related configuration options.

As a rule of thumb, implement a graceful shutdown in your game sever process when it receives the TERM signal from Kubernetes when the backing Pod goes into Termination state.

Be aware that if you use a variation of System.exit(0) after calling SDK.Shutdown(), your game server container may restart for a brief period, inline with our Health Checking policies.

If the SDK server receives a TERM signal before calling SDK.Shutdown(), the SDK server will stay alive for the period of the terminationGracePeriodSeconds until SDK.Shutdown() has been called.

Configuration Retrieval

GameServer()

This returns most of the backing GameServer configuration and Status. This can be useful for instances where you may want to know Health check configuration, or the IP and Port the GameServer is currently allocated to.

Since the GameServer contains an entire PodTemplate the returned object is limited to that configuration that was deemed useful. If there are areas that you feel are missing, please file an issue or pull request.

The easiest way to see what is exposed, is to check the sdk.proto, specifically at the message GameServer.

For language specific documentation, have a look at the respective source (linked above), and the examples.

WatchGameServer(function(gameserver){…})

This executes the passed in callback with the current GameServer details whenever the underlying GameServer configuration is updated. This can be useful to track GameServer > Status > State changes, metadata changes, such as labels and annotations, and more.

In combination with this SDK, manipulating Annotations and Labels can also be a useful way to communicate information through to running game server processes from outside those processes. This is especially useful when combined with GameServerAllocation applied metadata.

Since the GameServer contains an entire PodTemplate the returned object is limited to that configuration that was deemed useful. If there are areas that you feel are missing, please file an issue or pull request.

The easiest way to see what is exposed, is to check the sdk.proto, specifically at the message GameServer.

For language specific documentation, have a look at the respective source (linked above), and the examples.

Metadata Management

SetLabel(key, value)

This will set a Label value on the backing GameServer record that is stored in Kubernetes.

To maintain isolation, the key value is automatically prefixed with the value “agones.dev/sdk-”. This is done for two main reasons:

  • The prefix allows the developer to always know if they are accessing or reading a value that could have come, or may be changed by the client SDK. Much like private vs public scope in a programming language, the Code Blind SDK only gives you access to write to part of the set of labels and annotations that exist on a GameServer.
  • The prefix allows for a smaller attack surface if the GameServer container gets compromised. Since the game container is generally externally exposed, and the Code Blind project doesn’t control the binary that is run within it, limiting exposure if the game server becomes compromised is worth the extra development friction that comes with having this prefix in place.

Setting GameServer labels can be useful if you want information from your running game server process to be observable or searchable through the Kubernetes API.

SetAnnotation(key, value)

This will set an Annotation value on the backing GameServer record that is stored in Kubernetes.

To maintain isolation, the key value is automatically prefixed with “agones.dev/sdk-” for the same reasons as in SetLabel(…) above. The isolation is also important as Code Blind uses annotations on the GameServer as part of its internal processing.

Setting GameServer annotations can be useful if you want information from your running game server process to be observable through the Kubernetes API.

Counters And Lists

The Counters and Lists features in the SDK offer a flexible configuration for tracking various entities like players, rooms, and sessions.

Declared keys and default values for Counters and Lists are specified in GameServer.Spec.Counters and GameServer.Spec.Lists respectively.

Modified Counter and List values and capacities will be updated in GameServer.Status.Counters and GameServer.Status.Lists respectively.

Counters

All functions will return an error if the specified key is not predefined in the GameServer.Spec.Counters resource configuration.

Note: For Counters, the default setting for the capacity is preset to 1000. It is recommended to avoid configuring the capacity to max(int64), as doing so could cause problems with JSON Patch operations.

Alpha().GetCounterCount(key)

This function retrieves either the GameServer.Status.Counters[key].Count or the SDK awaiting-batch value for a given key, whichever is most up to date.

Alpha().SetCounterCount(key, amount)

This function sets the value of GameServer.Status.Counters[key].Count for the given key to the passed in amount. This operation overwrites any previous values and the new value cannot exceed the Counter’s capacity.

Alpha().IncrementCounter(key, amount)

This function increments GameServer.Status.Counters[key].Count for the given key by the passed in non-negative amount. The function returns an error if the Counter is already at capacity (at time of operation), indicating no increment will occur.

Alpha().DecrementCounter(key, amount)

This function decreases GameServer.Status.Counters[key].Count for the given key by the passed in non-negative amount. It returns an error if the Counter’s count is already at zero.

Alpha().SetCounterCapacity(key, amount)

This function sets the maximum GameServer.Status.Counters[key].Capacity for the given key by the passed in non-negative amount. A capacity value of 0 indicates no capacity limit.

Alpha().GetCounterCapacity(key)

This function retrieves either the GameServer.Status.Counters[key].Capacity or the SDK awaiting-batch value for the given key, whichever is most up to date.

Lists

All functions will return an error if the specified key is not predefined in the GameServer.Spec.Lists resource configuration.

Alpha().AppendListValue(key, value)

This function appends the specified string value to the List in GameServer.Status.Lists[key].Values.

An error is returned if the string already exists in the list or if the list is at capacity.

Alpha().DeleteListValue(key, value)

This function removes the specified string value from the List in GameServer.Status.Lists[key].Values.

An error is returned if the string does not exist in the list.

Alpha().SetListCapacity(key, amount)

This function sets the maximum capacity for the List at GameServer.Status.Lists[key].Capacity.

The capacity value is required to be between 0 and 1000.

Alpha().GetListCapacity(key)

This function retrieves either the GameServer.Status.Lists[key].Capacity or the SDK awaiting-batch value for the given key, whichever is most up to date.

Alpha().GetListValues(key)

This function retrieves either the GameServer.Status.Lists[key].Values or the SDK awaiting-batch values array for the given key, whichever is most up to date.

Alpha().ListContains(key, value)

Convenience function, which returns if the specific string value exists in the results of Alpha().GetListValues(key).

Alpha().GetListLength(key)

Convenience function, which retrieves the length of the results of Alpha().GetListValues(key).

Player Tracking

Alpha().PlayerConnect(playerID)

This function increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs.

GameServer.Status.Players.Count and GameServer.Status.Players.IDs are then set to update the player count and id list a second from now, unless there is already an update pending, in which case the update joins that batch operation.

PlayerConnect() returns true and adds the playerID to the list of playerIDs if this playerID was not already in the list of connected playerIDs.

If the playerID exists within the list of connected playerIDs, PlayerConnect() will return false, and the list of connected playerIDs will be left unchanged.

An error will be returned if the playerID was not already in the list of connected playerIDs but the player capacity for the server has been reached. The playerID will not be added to the list of playerIDs.

Alpha().PlayerDisconnect(playerID)

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs.

GameServer.Status.Players.Count and GameServer.Status.Players.IDs are then set to update the player count and id list a second from now, unless there is already an update pending, in which case the update joins that batch operation.

PlayerDisconnect() will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

If the playerID was not in the list of connected playerIDs, the call will return false, and the connected playerID list will be left unchanged.

Alpha().SetPlayerCapacity(count)

Update the GameServer.Status.Players.Capacity value with a new capacity.

Alpha().GetPlayerCapacity()

This function retrieves the current player capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().GetPlayerCount()

This function retrieves the current player count. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().IsPlayerConnected(playerID)

This function returns if the playerID is currently connected to the GameServer. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().GetConnectedPlayers()

This function returns the list of the currently connected player ids. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Writing your own SDK

If there isn’t an SDK for the language and platform you are looking for, you have several options:

gRPC Client Generation

If client generation is well supported by gRPC, then generate client(s) from the proto files found in the proto/sdk, directory and look at the current sdks to see how the wrappers are implemented to make interaction with the SDK server simpler for the user.

REST API Implementation

If client generation is not well supported by gRPC, or if there are other complicating factors, implement the SDK through the REST HTTP+JSON interface. This could be written by hand, or potentially generated from the Swagger/OpenAPI Specifications.

Finally, if you build something that would be usable by the community, please submit a pull request!

SDK Conformance Test

There is a tool SDK server Conformance checker which will run Local SDK server and record all requests your client is performing.

In order to check that SDK is working properly you should write simple SDK test client which would use all methods of your SDK.

Also to test that SDK client is receiving valid Gameserver data, your binary should set the same Label value as creation timestamp which you will receive as a result of GameServer() call and Annotation value same as gameserver UID received by Watch gameserver callback.

Complete list of endpoints which should be called by your test client is the following:

ready,allocate,setlabel,setannotation,gameserver,health,shutdown,watch

In order to run this test SDK server locally use:

SECONDS=30 make run-sdk-conformance-local

Docker container would timeout in 30 seconds and give your the comparison of received requests and expected requests

For instance you could run Go SDK conformance test and see how the process goes:

SDK_FOLDER=go make run-sdk-conformance-test

In order to add test client for your SDK, write sdktest.sh and Dockerfile. Refer to Golang SDK Conformance testing directory structure.

Building the Tools

If you wish to build the binaries from source the make target build-agones-sdk-binary will compile the necessary binaries for all supported operating systems (64 bit windows, linux and osx).

You can find the binaries in the bin folder in `cmd/sdk-server` once compilation is complete.

See Developing, Testing and Building Code Blind for more details.

1 - Unreal Engine Game Server Client Plugin

This is the Unreal Engine Code Blind Game Server Client Plugin.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Additional methods have been added for ease of use (both of which are enabled by default):

  • Connect
    • will call /gameserver till a succesful response is returned and then call /ready.
    • disabled by setting bDisableAutoConnect to true.
    • An event is broadcast with the GameServer data once the /gameserver call succeeds.
  • Health
    • calls /health endpoint on supplied rate
    • enabled by default with 10 second rate
    • disabled by default by setting HealthRateSeconds to 0.

Both of the above are automatically kicked off in the BeginPlay of the component.

Download

Download the source from the Releases Page or directly from GitHub.

Resources

Unreal is a game engine that is used by anyone from hobbyists all the way through to huge AAA Game Stuidos.

With this in mind there is a vast amount to learn to run a production game using Unreal, even before you get to learning how it integrates with Code Blind. If you want to kick the tires with a starter project you will probably be fine with one of the starter projects out of the box.

However as your Unreal/Code Blind project gets more advanced you will want to understand more about the engine itself and how it can be used to integrate with this project. There will be different ways of interacting via in Play In Editor (PIE) versus running as an actual dedicated game server packaged into a container.

There are few helpful links for latest Unreal Engine 5:

If you use Unreal Engine 4, There are few helpful links for it:

Getting Started

This is a SDK inspired by the REST API to the Code Blind sidecars that allows engineers to communicate with the sidecar from either C++ or Blueprints.

Getting the Code

Easiest way to get this code is to clone the repository and drop the entire plugin folder into your own Plugins folder. This runs the plugin as a Project plugin rather than an engine plugin.

We could however turn this into a marketplace plugin that can be retrived from the marketplace directly into the UE editor.

Using C++ (UE5/UE4)

  • Add Plugin (in your own .uproject file)
  "Plugins": [
    {
      "Enabled": true,
      "Name": "Code Blind"
    }
  ],
  • Add Plugin (in your own *.Build.cs)
PublicDependencyModuleNames.AddRange(
    new[]
    {
        "Code Blind",
    });
  • Add component in header
#include "AgonesComponent.h"

UPROPERTY(EditAnywhere, BlueprintReadWrite)
UAgonesComponent* AgonesSDK;
  • Initialize component in GameMode
#include "AgonesComponent.h"
#include "Classes.h"

ATestGameMode::ATestGameMode()
{
	AgonesSDK = CreateDefaultSubobject<UAgonesComponent>(TEXT("AgonesSDK"));
}
  • Use the Code Blind component to call PlayerReady
void APlatformGameSession::PostLogin(APlayerController* NewPlayer)
{
  // Empty brances are for callbacks on success and errror.
  AgonesSDK->PlayerConnect("netspeak-player", {}, {});
}

Using Blueprints (UE5)

  • Add Component to your Blueprint GameMode component

  • This will automatically call /health every 10 seconds and once /gameserver calls are succesful it will call /ready.

  • Accessing other functionality of Code Blind can be done via adding a node in Blueprints. actions

Using Blueprints (UE4)

  • Add Component to your Blueprint GameMode component

  • This will automatically call /health every 10 seconds and once /gameserver calls are succesful it will call /ready.

  • Accessing other functionality of Code Blind can be done via adding a node in Blueprints. actions

Configuration Options

A number of options can be altered via config files in Unreal these are supplied via Game configuration eg. DefaultGame.ini.

[/Script/Code Blind.AgonesComponent]
HttpPort=1337
HealthRateSeconds=5.0
bDisableAutoConnect=true

Unreal Hooks

Within the Unreal GameMode and GameSession exist a number of useful existing funtions that can be used to fit in with making calls out to Code Blind.

A few examples are:

  • RegisterServer to call SetLabel, SetPlayerCapacity
  • PostLogin to call PlayerConnect
  • NotifyLogout to call PlayerDisconnect

2 - Unity Game Server Client SDK

This is the Unity version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Additional methods have been added for ease of use:

  • Connect

Installation

The client SDK code can be manually downloaded and added to your project hierarchy.

It can also be imported into your project via the Unity Package Manager (UPM). To do that, open your project’s manifest.json file, and add the following line to the dependencies section:

{
  "dependencies": {
        "com.googleforgames.agones": "https://github.com/googleforgames/agones.git?path=/sdks/unity",
...

If you want a specific release, the dependency can be pinned to that version. For example:

"com.googleforgames.agones": "https://github.com/googleforgames/agones.git?path=/sdks/unity#v1.38.0",

Download

Download the source directly from GitHub.

Prerequisites

  • Unity >= 2018.x (.NET 4.x)

Usage

Import this script to your unity project and attach it to GameObject.

To begin working with the SDK, get an instance of it.

var agones = agonesGameObject.GetComponent<Code Blind.AgonesSdk>();

To connect to the SDK server, either local or when running on Code Blind, run the async Connect() method. This will wait for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = await agones.Connect();

To mark the game server as ready to receive player connections, call the async method Ready().

async void SomeMethod()
{
    bool ok = await agones.Ready();
}

To get the details on the backing GameServer call GameServer().

Will return null if there is an error in retrieving the GameServer record.

var gameserver = await agones.GameServer();

To mark the GameServer as Reserved for a duration call Reserve(TimeSpan duration).

ok = await agones.Reserve(duration);

To mark that the game session is completed and the game server should be shut down call Shutdown().

bool ok = await agones.Shutdown();

Similarly SetAnnotation(string key, string value) and SetLabel(string key, string value) are async methods that perform an action.

And there is no need to call Health(), it is automatically called.

To watch when the backing GameServer configuration changes call WatchGameServer(callback), where the delegate function callback will be executed every time the GameServer configuration changes.

agones.WatchGameServer(gameServer => Debug.Log($"Server - Watch {gameServer}"));

Player Tracking

To use alpha features use the AgonesAlphaSDK class.

var agones = agonesGameObject.GetComponent<Code Blind.AgonesAlphaSdk>();

Alpha: PlayerConnect

This method increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs. Returns true and adds the playerID to the list of playerIDs if the playerIDs was not already in the list of connected playerIDs.

bool ok = await agones.PlayerConnect(playerId);

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs. Will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

bool ok = await agones.PlayerDisconnect(playerId);

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

var capacity = 100;
bool ok = await agones.SetPlayerCapacity(capacity);

Alpha: GetPlayerCapacity

This function retrieves the current player capacity GameServer.Status.Players.Capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

long capacity = await agones.GetPlayerCapacity();

Alpha: GetPlayerCount

Returns the current player count

long count = await agones.GetPlayerCount();

Alpha: IsPlayerConnected

This returns if the playerID is currently connected to the GameServer. This is always accurate, even if the value hasn’t been updated to the GameServer status yet.

bool isConnected = await agones.IsPlayerConnected(playerId);

Alpha: GetConnectedPlayers

This returns a list of the playerIDs that are currently connected to the GameServer.

List<string> players = await agones.GetConnectedPlayers();

Settings

The properties for the Unity Code Blind SDK can be found in the Inspector.

  • Health Interval Second
    • Interval of the server sending a health ping to the Code Blind sidecar. (default: 5)
  • Health Enabled
    • Whether the server sends a health ping to the Code Blind sidecar. (default: true)
  • Log Enabled
    • Debug Logging Enabled. Debug logging for development of this Plugin. (default: false)

3 - C++ Game Server Client SDK

This is the C++ version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues

Installation

Download

Download the source from the Releases Page or directly from GitHub.

Building the Libraries from source

CMake is used to build SDK for all supported platforms (Linux/Window/macOS).

Prerequisites

  • CMake >= 3.15.0
  • Git
  • C++17 compiler

Dependencies

Code Blind SDK only depends on the gRPC library.

If CMake cannot find gRPC with find_package(), it downloads and builds gRPC. There are some extra prerequisites for OpenSSL on Windows, see documentation:

  • Perl
  • NASM

Note that OpenSSL is not used in Code Blind SDK, but it is required to have a successful build of gRPC.

Options

Following options are available:

  • AGONES_THIRDPARTY_INSTALL_PATH (default is CMAKE_INSTALL_PREFIX) - installation path for Code Blind prerequisites (used only if gRPC and Protobuf are not found by find_package)
  • AGONES_ZLIB_STATIC (default is ON) - use static version of zlib for gRPC

(Windows only):

  • AGONES_BUILD_THIRDPARTY_DEBUG (default is OFF) - build both debug and release versions of SDK’s prerequisites. Option is not used if you already have built gRPC.
  • AGONES_OPENSSL_CONFIG_STRING (default is VC-WIN64A) - arguments to configure OpenSSL build (documentation). Used only if OpenSSL and gRPC is built by Code Blind.

Linux / MacOS

mkdir -p .build
cd .build
cmake .. -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --target install

Windows

Building with Visual Studio:

md .build
cd .build
cmake .. -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --config Release --target install

Building with NMake

md .build
cd .build
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --target install

CMAKE_INSTALL_PREFIX may be skipped if it is OK to install Code Blind SDK to a default location (usually /usr/local or c:/Program Files/Code Blind).

CMake option -Wno-dev is specified to suppress CMP0048 deprecation warning for gRPC build.

If AGONES_ZLIB_STATIC is set to OFF, ensure that you have installed zlib. For Windows, it’s enough to copy zlib.dll near to gameserver executable. For Linux/Mac usually no actions are needed.

Usage

Using SDK

In CMake-based projects it’s enough to specify a folder where SDK is installed with CMAKE_PREFIX_PATH and use find_package(agones CONFIG REQUIRED) command. For example: cpp-simple. It may be useful to disable some protobuf warnings in your project.

Usage

The C++ SDK is specifically designed to be as simple as possible, and deliberately doesn’t include any kind of singleton management, or threading/asynchronous processing to allow developers to manage these aspects as they deem appropriate for their system.

We may consider these types of features in the future, depending on demand.

To begin working with the SDK, create an instance of it:

agones::SDK *sdk = new agones::SDK();

To connect to the SDK server, either local or when running on Code Blind, run the sdk->Connect() method. This will block for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = sdk->Connect();

To send a health check call sdk->Health(). This is a synchronous request that will return false if it has failed in any way. Read GameServer Health Checking for more details on the game server health checking strategy.

bool ok = sdk->Health();

To mark the game server as ready to receive player connections, call sdk->Ready(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Ready();
if (!status.ok()) { ... }

To mark the game server as allocated, call sdk->Allocate(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Allocate();
if (!status.ok()) { ... }

To mark the game server as reserved, call sdk->Reserve(seconds). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Reserve(std::chrono::seconds(N));
if (!status.ok()) { ... }

To mark that the game session is completed and the game server should be shut down call sdk->Shutdown(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Shutdown();
if (!status.ok()) { ... }

To set a Label on the backing GameServer call sdk->SetLabel(key, value). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->SetLabel("test-label", "test-value");
if (!status.ok()) { ... }

To set an Annotation on the backing GameServer call sdk->SetAnnotation(key, value). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

status = sdk->SetAnnotation("test-annotation", "test value");
if (!status.ok()) { ... }

To get the details on the backing GameServer call sdk->GameServer(&gameserver), passing in a agones::dev::sdk::GameServer* to push the results of the GameServer configuration into.

This function will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

agones::dev::sdk::GameServer gameserver;
grpc::Status status = sdk->GameServer(&gameserver);
if (!status.ok()) {...}

To get updates on the backing GameServer as they happen, call sdk->WatchGameServer([](const agones::dev::sdk::GameServer& gameserver){...}).

This will call the passed in std::function synchronously (this is a blocking function, so you may want to run it in its own thread) whenever the backing GameServer is updated.

sdk->WatchGameServer([](const agones::dev::sdk::GameServer& gameserver){
  std::cout << "GameServer Update:\n"                                 //
            << "\tname: " << gameserver.object_meta().name() << "\n"  //
            << "\tstate: " << gameserver.status().state() << "\n"
            << std::flush;
});

Next Steps

4 - Go Game Server Client SDK

This is the Go version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount✔️
CountersSetCounterCount✔️
CountersIncrementCounter✔️
CountersDecrementCounter✔️
CountersSetCounterCapacity✔️
CountersGetCounterCapacity✔️
ListsAppendListValue✔️
ListsDeleteListValue✔️
ListsSetListCapacity✔️
ListsGetListCapacity✔️
ListsListContains✔️
ListsGetListLength✔️
ListsGetListValues✔️
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Installation

go get the source, directly from GitHub

Usage

Review the GoDoc for usage instructions

5 - C# Game Server Client SDK

This is the C# version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Download

Download the source directly from GitHub.

Install using NuGet

  • Download the nuget package directly
  • Install the latest version using the Package Manager: Install-Package AgonesSDK
  • Install the latest version using the .NET CLI: dotnet add package AgonesSDK

To select a specific version, append --version, for example: --version 1.8.0 to either commands.

Prerequisites

  • .Net Standard 2.0 compliant framework.

Usage

Reference the SDK in your project & create a new instance of the SDK wrapper:

Initialization

To use the AgonesSDK, you will need to import the namespace by adding using Code Blind; at the beginning of your relevant files.

var agones = new AgonesSDK();

Connection

To connect to the SDK server, either locally or when running on Code Blind, run the ConnectAsync() method. This will wait for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = await agones.ConnectAsync();

Ready

To mark the game server as ready to receive player connections, call the async method ReadyAsync().

async void SomeMethod()
{
    var status = await agones.ReadyAsync();
}

Health

To send Health pings, call the async method HealthAsync()

await agones.HealthAsync();

GetGameServer

To get the details on the backing GameServer call GetGameServerAsync().

Will return null if there is an error in retrieving the GameServer record.

var gameserver = await agones.GetGameServerAsync();

Reserve

To mark the GameServer as Reserved for a duration call ReserveAsync(long duration).

long duration = 30;
var status = await agones.ReserveAsync(duration);

ShutDown

To mark that the game session is completed and the game server should be shut down call ShutdownAsync().

var status = await agones.ShutdownAsync();

SetAnnotation & SetLabel

Similarly SetAnnotation(string key, string value) and SetLabel(string key, string value) are async methods that perform an action & return a Status object.

WatchGameServer

To watch when the backing GameServer configuration changes call WatchGameServer(callback), where the delegate function callback of type Action<GameServer> will be executed every time the GameServer configuration changes. This process is non-blocking internally.

agonesSDK.WatchGameServer((gameServer) => { Console.WriteLine($"Server - Watch {gameServer}");});

Player Tracking

Alpha: PlayerConnect

This method increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs. Returns true and adds the playerID to the list of playerIDs if the playerIDs was not already in the list of connected playerIDs.

bool ok = await agones.Alpha().PlayerConnectAsync(playerId);

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs. Will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

bool ok = await agones.Alpha().PlayerDisconnectAsync(playerId);

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

var capacity = 100;
var status = await agones.Alpha().SetPlayerCapacityAsync(capacity);

Alpha: GetPlayerCapacity

This function retrieves the current player capacity GameServer.Status.Players.Capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

long cap = await agones.Alpha().GetPlayerCapacityAsync();

Alpha: GetPlayerCount

Returns the current player count

long count = await agones.Alpha().GetPlayerCountAsync();

Alpha: IsPlayerConnected

This returns if the playerID is currently connected to the GameServer. This is always accurate, even if the value hasn’t been updated to the GameServer status yet.

var playerId = "player1";
bool isConnected = await agones.Alpha().IsPlayerConnectedAsync(playerId);

Remarks

  • All requests other than ConnectAsync will wait for up to 15 seconds before giving up, time to wait can also be set in the constructor.
  • Default host & port are localhost:9357
  • Methods that do not return a data object such as GameServer will return a gRPC Grpc.Core.Status object. To check the state of the request, check Status.StatusCode & Status.Detail. Ex:
if(status.StatusCode == StatusCode.OK)
    //do stuff

6 - Node.js Game Server Client SDK

This is the Node.js version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Prerequisites

  • Node.js >= 10.13.0

Usage

Add the agones dependency to your project:

npm install @google-cloud/agones-sdk

If you need to download the source, rather than install from NPM, you can find it on GitHub.

To begin working with the SDK, create an instance of it.

const AgonesSDK = require('@google-cloud/agones-sdk');

let agonesSDK = new AgonesSDK();

To connect to the SDK server, either local or when running on Code Blind, run the async method sdk.connect(), which will resolve once connected or reject on error or if no connection can be made after 30 seconds.

await agonesSDK.connect();

To send a health check ping call health(errorCallback). The error callback is optional and if provided will receive an error whenever emitted from the health check stream.

agonesSDK.health((error) => {
	console.error('error', error);
});

To mark the game server as ready to receive player connections, call the async method ready(). The result will be an empty object in this case.

let result = await agonesSDK.ready();

Similarly shutdown(), allocate(), setAnnotation(key, value) and setLabel(key, value) are async methods that perform an action and return an empty result.

To get details of the backing GameServer call the async method getGameServer(). The result will be an object representing GameServer defined in `sdk.proto`.

let result = await agonesSDK.getGameServer();

To get updates on the backing GameServer as they happen, call watchGameServer(callback, errorCallback). The callback will be called with a parameter matching the result of getGameServer(). The error callback is optional and if provided will receive an error whenever emitted from the watch stream.

agonesSDK.watchGameServer((result) => {
	console.log('watch', result);
}, (error) => {
	console.error('error', error);
});

To mark the game server as reserved for a period of time, call the async method reserve(seconds). The result will be an empty object.

For more information, please read the SDK Overview, check out agonesSDK.js and also look at the Node.js example.

7 - Rust Game Server Client SDK

This is the Rust version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Prerequisites

Usage

Add this crate to dependencies section in your Cargo.toml.

Also note that the SDK is async only, so you will need an async runtime to execute the futures exposed by the SDK. It is recommended to use tokio as the SDK already depends on tokio due to its choice of gRPC library, tonic.

[dependencies]
agones = "1.34.0"
tokio = { version = "1.32.0", features = ["macros", "sync"] }

To begin working with the SDK, create an instance of it.

use std::time::Duration;

#[tokio::main]
async fn main() {
    let mut sdk = agones::Sdk::new(None /* default port */, None /* keep_alive */)
        .await
        .expect("failed to connect to SDK server");
}

To send health checks, call sdk.health_check, which will return a tokio::sync::mpsc::Sender::<()> which will send a health check every time a message is posted to the channel.

let health = sdk.health_check();
if health.send(()).await.is_err() {
    eprintln!("the health receiver was closed");
}

To mark the game session as ready call sdk.ready().

sdk.ready().await?;

To mark the game server as reserved for a period of time, call sdk.reserve(duration).

sdk.reserve(Duration::new(5, 0)).await?;

To mark that the game session is completed and the game server should be shut down call sdk.shutdown().

if let Err(e) = sdk.shutdown().await {
    eprintln!("Could not run Shutdown: {}", e);
}

To set a Label on the backing GameServer call sdk.set_label(key, value).

sdk.set_label("test-label", "test-value").await?;

To set an Annotation on the backing GameServer call sdk.set_annotation(key, value).

sdk.set_annotation("test-annotation", "test value").await?;

To get details of the backing GameServer call sdk.get_gameserver().

The function will return an instance of agones::types::GameServer including GameServer configuration info.

let gameserver = sdk.get_gameserver().await?;

To get updates on the backing GameServer as they happen, call sdk.watch_gameserver.

This will stream updates and endlessly until the stream is closed, so it is recommended to push this into its own async task.

let _watch = {
    // We need to clone the SDK as we are moving it to another task
    let mut watch_client = sdk.clone();
    // We use a simple oneshot to signal to the task when we want it to shutdown
    // and stop watching the gameserver update stream
    let (tx, mut rx) = tokio::sync::oneshot::channel::<()>();

    tokio::task::spawn(async move {
        println!("Starting to watch GameServer updates...");
        match watch_client.watch_gameserver().await {
            Err(e) => eprintln!("Failed to watch for GameServer updates: {}", e),
            Ok(mut stream) => loop {
                tokio::select! {
                    // We've received a new update, or the stream is shutting down
                    gs = stream.message() => {
                        match gs {
                            Ok(Some(gs)) => {
                                println!("GameServer Update, name: {}", gs.object_meta.unwrap().name);
                                println!("GameServer Update, state: {}", gs.status.unwrap().state);
                            }
                            Ok(None) => {
                                println!("Server closed the GameServer watch stream");
                                break;
                            }
                            Err(e) => {
                                eprintln!("GameServer Update stream encountered an error: {}", e);
                            }
                        }

                    }
                    // The watch is being dropped so we're going to shutdown the task
                    // and the watch stream
                    _ = &mut rx => {
                        println!("Shutting down GameServer watch loop");
                        break;
                    }
                }
            },
        }
    });

    tx
};

For more information, please read the SDK Overview, check out agones sdk implementation and also look at the Rust example.

8 - REST Game Server Client API

This is the REST version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounter✔️
CountersUpdateCounter✔️
ListsGetList✔️
ListsUpdateList✔️
ListsAddListValue✔️
ListsRemoveListValue✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingSetPlayerCapacity✔️
Player TrackingPlayerConnect✔️
Player TrackingGetConnectedPlayers✔️
Player TrackingIsPlayerConnected✔️
Player TrackingGetPlayerCount✔️
Player TrackingPlayerDisconnect✔️

The REST API can be accessed from http://localhost:${AGONES_SDK_HTTP_PORT}/ from the game server process. AGONES_SDK_HTTP_PORT is an environment variable automatically set for the game server process by Code Blind to support binding the REST API to a dynamic port. It is advised to use the environment variable rather than a hard coded port; otherwise your game server will not be able to contact the SDK server if it is configured to use a non-default port.

Generally the REST interface gets used if gRPC isn’t well supported for a given language or platform.

Generating clients

While you can hand write REST integrations, we also have a set of generated OpenAPI/Swagger definitions available. This means you can use OpenAPI/Swagger tooling to generate clients as well, if you need them.

For example, to create a cpp client for the stable sdk endpoints (to be run in the agones home directory):

docker run --rm -v ${PWD}:/local swaggerapi/swagger-codegen-cli generate -i /local/sdks/swagger/sdk.swagger.json  -l cpprest -o /local/out/cpp

The same could be run for alpha.swagger.json and beta.swagger.json as required.

You can read more about OpenAPI/Swagger code generation in their Command Line Tool Documentation

Reference

Lifecycle Management

Ready

Call when the GameServer is ready to accept connections

  • Path: /ready
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/ready

Health

Send a Empty every d Duration to declare that this GameServer is healthy

  • Path: /health
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/health

Reserve

Move Gameserver into a Reserved state for a certain amount of seconds for the future allocation.

  • Path: /reserve
  • Method: POST
  • Body: {"seconds": "5"}
Example
curl -d '{"seconds": "5"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/reserve

Allocate

With some matchmakers and game matching strategies, it can be important for game servers to mark themselves as Allocated. For those scenarios, this SDK functionality exists.

Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/allocate

Shutdown

Call when the GameServer session is over and it’s time to shut down

  • Path: /shutdown
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/shutdown

Configuration Retrieval

GameServer

Call when you want to retrieve the backing GameServer configuration details

  • Path: /gameserver
  • Method: GET
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/gameserver

Response:

{
    "object_meta": {
        "name": "local",
        "namespace": "default",
        "uid": "1234",
        "resource_version": "v1",
        "generation": "1",
        "creation_timestamp": "1531795395",
        "annotations": {
            "annotation": "true"
        },
        "labels": {
            "islocal": "true"
        }
    },
    "status": {
        "state": "Ready",
        "address": "127.0.0.1",
        "ports": [
            {
                "name": "default",
                "port": 7777
            }
        ]
    }
}

Watch GameServer

Call this when you want to get updates of when the backing GameServer configuration is updated.

These updates will come as newline delimited JSON, send on each update. To that end, you will want to keep the http connection open, and read lines from the result stream and and process as they come in.

curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/watch/gameserver

Response:

{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}
{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}
{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}

The Watch GameServer stream is also exposed as a WebSocket endpoint on the same URL and port as the HTTP watch/gameserver API. This endpoint is provided as a convienence for streaming data to clients such as Unreal that support WebSocket but not HTTP streaming, and HTTP streaming should be used instead if possible.

An example command that uses the WebSocket endpoint instead of streaming over HTTP is:

curl -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Key: ExampleKey1234567890===" -H "Sec-WebSocket-Version: 13" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/watch/gameserver

The data returned from this endpoint is delimited by the boundaries of a WebSocket payload as defined by RFC 6455, section 5.2. When reading from this endpoint, if your WebSocket client does not automatically handle frame reassembly (e.g. Unreal), make sure to read to the end of the WebSocket payload (as defined by the FIN bit) before attempting to parse the data returned. This is transparent in most clients.

Metadata Management

Set Label

Apply a Label with the prefix “agones.dev/sdk-” to the backing GameServer metadata.

See the SDK SetLabel documentation for restrictions.

Example
curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/metadata/label

Set Annotation

Apply an Annotation with the prefix “agones.dev/sdk-” to the backing GameServer metadata

Example
curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/metadata/annotation

Counters and Lists

Counters

In all the Counter examples, we retrieve the counter under the key rooms as if it was previously defined in GameServer.Spec.counters[room].

For your own Counter REST requests, replace the value rooms with your own key in the path.

Alpha: GetCounter

This function retrieves a specified counter by its key, rooms, and returns its information.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/counters/rooms

Response:

{"name":"rooms", "count":"1", "capacity":"10"}
Alpha: UpdateCounter

This function updates the properties of the counter with the key rooms, such as its count and capacity, and returns the updated counter details.

Example
curl -d '{"count": "5", "capacity": "11"}' -H "Content-Type: application/json" -X PATCH http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/counters/rooms

Response:

{"name":"rooms", "count":"5", "capacity":"11"}

Lists

In all the List examples, we retrieve the list under the key players as if it was previously defined in GameServer.Spec.lists[players].

For your own List REST based requests, replace the value players with your own key in the path.

Alpha: GetList

This function retrieves the list’s properties with the key players, returns the list’s information.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players

Response:

{"name":"players", "capacity":"100", "values":["player0", "player1", "player2"]}     
Alpha: UpdateList

This function updates the list’s properties with the key players, such as its capacity and values, returns the updated list details. This will overwrite all existing List.Values with the update list request values. Use addValue or removeValue for modifying the List.Values field.

Example
curl -d '{"capacity": "120", "values": ["player3", "player4"]}' -H "Content-Type: application/json" -X PATCH http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players

Response:

{"name":"players", "capacity":"120", "values":["player3", "player4"]}
Alpha: AddListValue

This function adds a new value to a list with the key players and returns the list with this addition.

Example
curl -d '{"value": "player9"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players:addValue

Response:

{"name":"players", "capacity":"120", "values":["player3", "player4", "player9"]}
Alpha: RemoveListValue

This function removes a value from the list with the key players and returns updated list.

Example
curl -d '{"value": "player3"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players:removeValue

Response:

{"name":"players", "capacity":"120", "values":["player4", "player9"]}

Player Tracking

Alpha: PlayerConnect

This function increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs.

Example
curl -d '{"playerID": "uzh7i"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connect

Response:

{"bool":true}

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs.

Example
curl -d '{"playerID": "uzh7i"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/disconnect

Response:

{"bool":true}

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

Example
curl -d '{"count": 5}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/capacity

Alpha: GetPlayerCapacity

This function retrieves the current player capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -d '{}' -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/capacity

Response:

{"count":"5"}

Alpha: GetPlayerCount

This function retrieves the current player count. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/count

Response:

{"count":"2"}

Alpha: IsPlayerConnected

This function returns if the playerID is currently connected to the GameServer. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connected/uzh7i

Response:

{"bool":true}

Alpha: GetConnectedPlayers

This function returns the list of the currently connected player ids. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connected

Response:

{"list":["uzh7i","3zh7i"]}

9 - Local Development

Working against the SDK without having to run a full kubernetes stack

When the game server is running on Code Blind, the SDK communicates over TCP to a small gRPC server that Code Blind coordinated to run in a container in the same network namespace as it - usually referred to in Kubernetes terms as a “sidecar”.

Therefore, when developing locally, we also need a process for the SDK to connect to!

To do this, we can run the same binary (the SDK Server) that runs inside Code Blind, but pass in a flag to run it in “local mode”. Local mode means that the sidecar binary will not try to connect to anything, and will just send log messages to stdout and persist local state in memory so that you can see exactly what the SDK in your game server is doing, and can confirm everything works.

Running the SDK Server

To run the SDK Server, you will need a copy of the binary. This can either be done by downloading a prebuilt binary or running from source code. This guide will focus on running from the prebuilt binary, but details about running from source code can be found below.

To run the prebuilt binary, for the latest release, you will need to download agonessdk-server-1.38.0.zip , and unzip it. You will find the executables for the SDK server, for each type of operating system.

  • MacOS
    • sdk-server.darwin.amd64
    • sdk-server.darwin.arm64
  • Linux
    • sdk-server.linux.amd64
    • sdk-server.linux.arm64
  • Windows
    • sdk-server.windows.amd64.exe

Running In “Local Mode”

To run in local mode, pass the flag --local to the executable.

For example:

./sdk-server.linux.amd64 --local

You should see output similar to the following:

{"ctlConf":{"Address":"localhost","IsLocal":true,"LocalFile":"","Delay":0,"Timeout":0,"Test":"","GRPCPort":9357,"HTTPPort":9358},"message":"Starting sdk sidecar","severity":"info","source":"main","time":"2019-10-30T21:44:37.973139+03:00","version":"1.1.0"}
{"grpcEndpoint":"localhost:9357","message":"Starting SDKServer grpc service...","severity":"info","source":"main","time":"2019-10-30T21:44:37.974585+03:00"}
{"httpEndpoint":"localhost:9358","message":"Starting SDKServer grpc-gateway...","severity":"info","source":"main","time":"2019-10-30T21:44:37.975086+03:00"}
{"message":"Ready request has been received!","severity":"info","time":"2019-10-30T21:45:47.031989+03:00"}
{"message":"gameserver update received","severity":"info","time":"2019-10-30T21:45:47.03225+03:00"}
{"message":"Shutdown request has been received!","severity":"info","time":"2019-10-30T21:46:18.179341+03:00"}
{"message":"gameserver update received","severity":"info","time":"2019-10-30T21:46:18.179459+03:00"}

Enabling Feature Gates

For development and testing purposes, you might want to enable specific features gates in the local SDK Server.

To do this, you can either set the FEATURE_GATES environment variable or use the --feature-gates command line parameter like so, with the same format as utilised when configuring it on a Helm install.

For example:

./sdk-server.linux.amd64 --local --feature-gates Example=true

or

FEATURE_GATES=Example=true ./sdk-server.linux.amd64 --local

Providing your own GameServer configuration for local development

By default, the local sdk-server will create a default GameServer configuration that is used for GameServer() and WatchGameServer() SDK calls. If you wish to provide your own configuration, as either yaml or json, this can be passed through as either --file or -f along with the --local flag.

If the GamerServer configuration file is changed while the local server is running, this will be picked up by the local server, and will change the current active configuration, as well as sending out events for WatchGameServer(). This is a useful way of testing functionality, such as changes of state from Ready to Allocated in your game server code.

For example:

wget https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver.yaml
./sdk-server.linux.amd64 --local -f ./gameserver.yaml
{"ctlConf":{"Address":"localhost","IsLocal":true,"LocalFile":"./gameserver.yaml","Delay":0,"Timeout":0,"Test":"","GRPCPort":9357,"HTTPPort":9358},"message":"Starting sdk sidecar","severity":"info","source":"main","time":"2019-10-30T21:47:45.742776+03:00","version":"1.1.0"}
{"filePath":"/Users/alexander.apalikov/Downloads/agonessdk-server-1.1.0/gameserver.yaml","message":"Reading GameServer configuration","severity":"info","time":"2019-10-30T21:47:45.743369+03:00"}
{"grpcEndpoint":"localhost:9357","message":"Starting SDKServer grpc service...","severity":"info","source":"main","time":"2019-10-30T21:47:45.759692+03:00"}
{"httpEndpoint":"localhost:9358","message":"Starting SDKServer grpc-gateway...","severity":"info","source":"main","time":"2019-10-30T21:47:45.760312+03:00"}

Changing State of a Local GameServer

Some SDK calls would change the GameServer state according to GameServer State Diagram. Also local SDK server would persist labels and annotations updates.

Here is a complete list of these commands: ready, allocate, setlabel, setannotation, shutdown, reserve.

For example call to Reserve() for 30 seconds would change the GameServer state to Reserve and if no call to Allocate() occurs it would return back to Ready state after this period.

All changes to the GameServer state could be observed and retrieved using Watch() or GameServer() methods using GameServer SDK.

Example of using HTTP gateway locally:

curl -X POST "http://localhost:9358/ready" -H "accept: application/json" -H "Content-Type: application/json" -d "{}"
{}
curl -GET "http://localhost:9358/gameserver" -H "accept: application/json"
{"object_meta":{"creation_timestamp":"-62135596800"},"spec":{"health":{}},"status":{"state":"Ready"}}
curl -X PUT "http://localhost:9358/metadata/label" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"key\": \"foo\", \"value\": \"bar\"}"
curl -GET "http://localhost:9358/gameserver" -H "accept: application/json"
{"object_meta":{"creation_timestamp":"-62135596800","labels":{"agones.dev/sdk-foo":"bar"}},"spec":{"health":{}},"status":{"state":"Ready"}}

Running Local Mode in a Container

Once you have your game server process in a container, you may also want to test the container build locally as well.

Since the production agones-sdk binary has the --local mode built in, you can also use the production container image locally as well!

Since the SDK and your game server container need to share a port on localhost, one of the easiest ways to do that is to have them both run using the host network, like so:

In one shell run:

docker run --network=host --rm us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0 --local

You should see a similar output to what you would if you were running the binary directly, i.e. outside a container.

Then in another shell, start your game server container:

docker run --network=host --rm <your image here>

If you want to mount a custom gameserver.yaml, this is also possible:

wget https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver.yaml
# required so that the `agones` user in the container can read the file
chmod o+r gameserver.yaml
docker run --network=host --rm -v $(pwd)/gameserver.yaml:/tmp/gameserver.yaml us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0 --local -f /tmp/gameserver.yaml

If you run Docker on a OS that doesn’t run Docker natively or in a VM, such as on Windows or macOS, you may want to to run the ClientSDK and your game server container together with Docker Compose. To do so, create a docker-compose.yaml file setup with a network overlay shared between them:

version: '3'
services:
  gameserver:
    build: . # <path to build context>
    ports:
      - "127.0.0.1:7777:7777/udp"

  sdk-server:
    image: "us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0"
    command: --local -f /gs_config
    network_mode: service:gameserver # <shared network between sdk and game server>
    configs:
      - gs_config

configs:
  gs_config:
    file: ./gameserver.yaml

Run docker-compose

docker-compose up --build

Running from source code instead of prebuilt binary

If you wish to run from source rather than pre-built binaries, that is an available alternative. You will need Go installed and will need to clone the Code Blind GitHub repo.

Disclaimer: Code Blind is run and tested with the version of Go specified by the GO_VERSION variable in the project’s build Dockerfile. Other versions are not supported, but may still work.

Your cloned repository is best switched to the latest specific release’s branch/tag. For example:

git clone https://github.com/googleforgames/agones.git
cd agones
git checkout release-1.38.0

With Go installed and the Code Blind repository cloned, the SDK Server can be run with the following command (from the Code Blind clone directory):

go run cmd/sdk-server/main.go --local

Commandline flags (e.g. --local) are exactly the same as command line flags when utilising a pre-built binary.

Next Steps:

  • Learn how to connect your local development game server binary into a running Code Blind Kubernetes cluster for even more live development options with an out of cluster dev server.