SDK Quickstart
Integrate your dedicated server with GameFlow in a handful of steps. Pick your language once; the selection follows you across every step and across the rest of the docs.
The flow is the same everywhere: connect → ready → track players → shut down. Health reporting starts on its own once you call ready(); you never ping anything yourself.
1. Install
- Unity (C#)
- Unreal (C++)
- Godot
- TypeScript
- Rust
- Go
Add the package via the Unity Package Manager (Window → Package Manager → + → Add package from git URL):
https://github.com/GameFlowGG/gameflow-gameserver-sdk.git?path=/sdk/unity/Packages/gg.gameflow.gameserver#sdk/unity/v0.1.0
Requires Unity 2022.3+ with Api Compatibility Level = .NET Standard 2.1. A Dedicated Server build is recommended for production.
The plugin lives in the SDK repository under sdk/unreal/GameFlow/. Clone the repo and copy that folder into your project's Plugins/ directory, then add the module dependency:
git clone --depth 1 --branch sdk/unreal/v0.1.0 https://github.com/GameFlowGG/gameflow-gameserver-sdk.git
cp -r gameflow-gameserver-sdk/sdk/unreal/GameFlow /path/to/YourProject/Plugins/GameFlow
// YourModule.Build.cs
PublicDependencyModuleNames.AddRange(new string[] {
"Core", "CoreUObject", "Engine",
"GameFlowUnreal",
});
Requires Unreal Engine 5.3+ and a Dedicated Server build target for production.
The addon lives in the SDK repository under sdk/godot/addons/gameflow/. Clone the repo, then copy that folder into your project's addons/ directory:
git clone --depth 1 --branch godot-v0.1.2 https://github.com/GameFlowGG/gameflow-gameserver-sdk.git
cp -r gameflow-gameserver-sdk/sdk/godot/addons/gameflow /path/to/YourProject/addons/gameflow
The SDK classes are plain class_name scripts, so enabling the plugin is optional. Requires Godot 4.4+.
npm install @gameflow.gg/gameserver-sdk
Requires Node 18.17+. Zero runtime dependencies.
[dependencies]
gameflow-gameserver-sdk = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
Engine-agnostic: works with Bevy, Fyrox, macroquad, or a bare tokio server.
go get github.com/GameFlowGG/gameflow-gameserver-sdk/sdk/go
Requires Go 1.23+. No third-party dependencies: standard library only.
2. Connect and signal readiness
Connect on startup, start listening on the assigned port, then call ready() only when your server can actually accept connections. Until then the platform will not send players to it.
- Unity (C#)
- Unreal (C++)
- Godot
- TypeScript
- Rust
- Go
var runner = GameFlowRunner.Create();
var gf = new GameFlowClient(new GameFlowOptions {
Logger = new UnityDebugLogger(),
Dispatcher = runner.Dispatcher,
});
runner.Bind(gf);
await gf.Start(); // connects (with retries) or falls back to local mode
// start listening on your transport, then:
await gf.Ready(); // health reporting starts automatically
The GameFlowRunner is a MonoBehaviour that delivers callbacks on Unity's main thread. Headless code can skip it and use GameFlowClient directly.
// In BeginPlay (server only):
UGameFlowSubsystem* GF = GetGameInstance()->GetSubsystem<UGameFlowSubsystem>();
GF->Start([GF](FGameFlowError Err) {
if (!Err.IsOk()) { return; }
// start listening, then mark ready. Health heartbeats start here:
GF->Ready([](FGameFlowError Err) { /* server live on GameFlow */ });
});
var gf := GameFlow.new()
func _ready() -> void:
var res := await gf.start()
if not res.ok:
push_error("gameflow: %s" % res.message)
return
# start listening on gf.ports.default, then:
await gf.ready() # health reporting starts automatically
Create one GameFlow instance for the lifetime of the process; an autoload is the natural place.
import { GameFlow } from '@gameflow.gg/gameserver-sdk';
const gf = await GameFlow.connect();
// Listen on the platform-assigned port, then signal readiness.
myServer.listen(gf.ports.default ?? 7777);
await gf.ready(); // health reporting starts automatically
use gameflow::GameFlow;
#[tokio::main]
async fn main() -> gameflow::Result<()> {
let gf = GameFlow::connect().await?;
// ... start your server on gf.ports().default() ...
gf.ready().await?; // health reporting starts automatically
Ok(())
}
ctx := context.Background()
gf, err := gameflow.Connect(ctx)
if err != nil {
log.Fatal(err)
}
// ... start your server on gf.Ports().Default() ...
if err := gf.Ready(ctx); err != nil { // health reporting starts automatically
log.Fatal(err)
}
3. Track players
Register a session when a player joins and unregister it when they leave. Use any stable unique id (session id, account id).
This matters beyond dashboards: GameFlow shuts down servers that report zero players past your organization's idle timeout, so a server that never reports players will be reaped.
- Unity (C#)
- Unreal (C++)
- Godot
- TypeScript
- Rust
- Go
try {
await gf.Players.Connect(sessionId);
} catch (ServerFullException e) {
Kick(sessionId, $"server full (capacity {e.Capacity})");
}
await gf.Players.Disconnect(sessionId);
var n = gf.Players.Count; // synchronous reads
// In PostLogin:
GF->ConnectPlayer(SessionId, [](FGameFlowError Err) {
// Err.Code can be SERVER_FULL or PLAYER_ALREADY_CONNECTED
});
// In Logout:
GF->DisconnectPlayer(SessionId, [](FGameFlowError Err, bool bWasTracked) { /* ... */ });
GF->PlayerCount(); // BlueprintPure synchronous read
# when a player joins (named track/untrack because `connect` collides with Godot signals)
var res := await gf.players.track(session_id)
if not res.ok and res.code == GameFlowResult.SERVER_FULL:
kick(session_id, "server full (capacity %d)" % res.capacity)
# when a player leaves
await gf.players.untrack(session_id)
gf.players.count() # synchronous reads from the local cache
import { ServerFullError } from '@gameflow.gg/gameserver-sdk';
// when a player joins
try {
await gf.players.connect(client.sessionId);
} catch (err) {
if (err instanceof ServerFullError) client.reject('server full');
}
// when a player leaves
await gf.players.disconnect(client.sessionId);
gf.players.count(); // current player count (synchronous)
gf.players.list(); // connected session ids
gf.players.capacity(); // max players configured for your game
// when a player joins / leaves
gf.players().connect("session-1").await?;
gf.players().disconnect("session-1").await?;
gf.players().count(); // synchronous reads
connect() returns ServerFull / PlayerAlreadyConnected errors you can branch on; disconnect() resolves false for unknown ids.
gf.Players().Connect(ctx, "session-1") // when a player joins
gf.Players().Disconnect(ctx, "session-1") // when a player leaves
gf.Players().Count() // synchronous reads from the local cache
Branch on errors with gameflow.CodeOf(err) or errors.Is(err, gameflow.ErrServerFull).
4. Use the launch payload (optional)
Servers can be launched with an opaque payload (match config, team rosters, anything). The SDK treats it as a string, so parse it however you produced it.
- Unity (C#)
- Unreal (C++)
- Godot
- TypeScript
- Rust
- Go
var payload = await gf.Payload(); // string (null when none)
gf.OnPayloadChange(next => {
// fires when the platform assigns this server to a new match
});
GF->GetPayload([](FGameFlowError Err, FString Payload) { /* parse it */ });
// Or bind the Blueprint-assignable event:
GF->OnPayloadChanged.AddDynamic(this, &AMyGameMode::HandlePayloadChanged);
var res := await gf.payload()
if res.value != null:
var match_config = JSON.parse_string(res.value)
gf.on_payload_change(func(next): pass) # match (re)assignment
const payload = await gf.payload(); // string | undefined
const match = payload ? JSON.parse(payload) : null;
gf.onPayloadChange((next) => {
// fires when the platform assigns this server to a new match
});
let payload: Option<String> = gf.payload().await?;
gf.on_payload_change(|next| {
// fires when the platform assigns this server to a new match
});
payload, _ := gf.Payload(ctx) // string
gf.OnPayloadChange(func(next string) {
// fires when the platform assigns this server to a new match
})
5. Shut down cleanly
When a server is stopped, the platform sends SIGTERM and force-kills the container about 45 seconds later. Drain and call shutdown() well within that budget. Also call it yourself when a match ends and the server is done.
- Unity (C#)
- Unreal (C++)
- Godot
- TypeScript
- Rust
- Go
await gf.Shutdown(); // when the match ends; idempotent
The GameFlowRunner also sends a clean shutdown automatically on application quit.
// Automatic: UGameFlowSubsystem::Deinitialize() shuts down on map unload / exit.
// Call explicitly only if you need to end early:
GF->Shutdown([](FGameFlowError Err) { /* ... */ });
await gf.shutdown() # when the match ends; idempotent
process.on('SIGTERM', async () => {
myServer.close();
await gf.shutdown();
process.exit(0);
});
gf.shutdown().await?; // when the match ends; idempotent
gf.Shutdown(ctx) // when the match ends; idempotent
6. Ship it
Package your server with a Dockerfile at the build root (run as UID 1000, expose your port) and upload it through the GameFlow dashboard or CLI. See Creating a Game.
Before you upload, test everything on your machine first; the same binary runs unchanged off-platform. See Local development.