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
- TypeScript
- Godot
- Rust
- Go
- Unity (C#)
- Unreal (C++)
npm install @gameflow.gg/gameserver-sdk
Requires Node 18.17+. Zero runtime dependencies.
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+.
[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.
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.
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.
- TypeScript
- Godot
- Rust
- Go
- Unity (C#)
- Unreal (C++)
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
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.
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)
}
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 */ });
});
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.
- TypeScript
- Godot
- Rust
- Go
- Unity (C#)
- Unreal (C++)
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 (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
// 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).
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
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.
- TypeScript
- Godot
- Rust
- Go
- Unity (C#)
- Unreal (C++)
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
});
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
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
})
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);
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.
- TypeScript
- Godot
- Rust
- Go
- Unity (C#)
- Unreal (C++)
process.on('SIGTERM', async () => {
myServer.close();
await gf.shutdown();
process.exit(0);
});
await gf.shutdown() # when the match ends; idempotent
gf.shutdown().await?; // when the match ends; idempotent
gf.Shutdown(ctx) // when the match ends; idempotent
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) { /* ... */ });
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.