Integrating Nakama
Nakama is an open-source game backend by Heroic Labs. It handles player accounts, leaderboards, matchmaking, in-app purchases, and real-time multiplayer features.
Gameye provides a native Fleet Manager implementation for Nakama, the nakama-fleetmanager Go package. When Nakama’s matchmaker finds a group of players, your MatchmakerMatched hook calls the Fleet Manager, which starts a dedicated server session via the Session API and registers the matched players with it. One helper call then sends each player the server’s IP and port as a Nakama notification.
How it works
Section titled “How it works”[Nakama Matchmaker] ↓ match found[MatchmakerMatched hook] → fleetManager.Create(...) ↓ POST /session, PUT /session/player/join[Gameye Session API] ↓ container starts[Create callback] → fleetmanager.NotifyConnectionInfo(...) ↓ "gameye_match" notification (host, port, session id)[Players] ←→ [Dedicated game server]The Fleet Manager starts sessions, joins the matched players, stops sessions it gives up on, and keeps Nakama’s view of running sessions in sync with Gameye. Gameye stops each session when its TTL runs out.
Prerequisites
Section titled “Prerequisites”- Nakama 3.39 or later (the package’s example is pinned to Nakama 3.41)
- nakama-common v1.46 or later, matching your Nakama release (v1.48.0 for Nakama 3.41)
- A Gameye account (sign up) and an API token with the
session:start,session:readandsession:stopscopes - An application in Gameye with your game server image pushed to it
- No warm pool for that application and region if your server reads per-match environment variables (see Warm pools and env)
A Nakama Go plugin must be built with the heroiclabs/nakama-pluginbuilder image whose tag matches your heroiclabs/nakama image, and with the same versions of dependencies the server uses.
Installation
Section titled “Installation”Add the package to your Nakama server module and pin nakama-common to your Nakama release:
go mod vendorConfiguration
Section titled “Configuration”The example module reads its configuration from Nakama’s runtime.env in your Nakama config file:
| Variable | Required | Description |
|---|---|---|
GAMEYE_API_TOKEN | Yes | Your Gameye API token |
GAMEYE_API_IMAGE | Yes | Your application name in Gameye |
GAMEYE_API_IMAGE_VERSION | Yes | The image tag to run |
GAMEYE_API_REGION | Yes | Target region (see Region Selection) |
GAMEYE_API_URL | No | Session API URL. Defaults to the self-serve platform, https://api.sandbox-gameye.gameye.net. Production contracts use https://api.production-gameye.gameye.net |
GAMEYE_API_TTL | No | Maximum session lifetime, e.g. 30m (default) or 2h |
GAMEYE_API_PORT | No | Container port players connect to, e.g. 7777/udp. Set it when your image exposes more than one port |
runtime: env: - "GAMEYE_API_TOKEN=your-api-token" - "GAMEYE_API_IMAGE=your-application" - "GAMEYE_API_IMAGE_VERSION=1.0.0" - "GAMEYE_API_REGION=europe" - "GAMEYE_API_PORT=7777/udp"Registration in InitModule
Section titled “Registration in InitModule”Create the Fleet Manager in your module’s InitModule and register it:
fm, err := fleetmanager.NewGameyeFleetManager(ctx, fleetmanager.GameyeConfig{ ApiToken: env["GAMEYE_API_TOKEN"], Image: env["GAMEYE_API_IMAGE"], Version: env["GAMEYE_API_IMAGE_VERSION"], Region: env["GAMEYE_API_REGION"], Ttl: env["GAMEYE_API_TTL"], Port: env["GAMEYE_API_PORT"],}, logger, db, initializer, nk)if err != nil { return err}if err := initializer.RegisterFleetManager(fm); err != nil { return err}Starting a server for a match
Section titled “Starting a server for a match”The matchmaker flow has three steps.
- Create. In
MatchmakerMatched, callfm.Createwith the matched user ids. It returns{"session_id": <id>}at once and starts the session in the background. - Callback. When the session is running, the callback receives
runtime.CreateSuccessand the instance. The players are already joined to the session, so don’t callJoinagain. Nakama cancels the hook’s context when the hook returns, so use a new context in the callback. - Notify. Call
fleetmanager.NotifyConnectionInfo. Each player gets a notification with subjectgameye_matchwhose content holdshost,portandsession_id.
initializer.RegisterMatchmakerMatched(func(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, entries []runtime.MatchmakerEntry) (string, error) { userIds := make([]string, 0, len(entries)) for _, e := range entries { userIds = append(userIds, e.GetPresence().GetUserId()) }
callback := func(status runtime.FmCreateStatus, instance *runtime.InstanceInfo, _ []*runtime.SessionInfo, _ map[string]any, err error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if status != runtime.CreateSuccess { logger.Warn("no game server for %v: %v", userIds, err) return } _ = fleetmanager.NotifyConnectionInfo(ctx, nk, userIds, instance, nil, true) }
metadata := map[string]any{ fleetmanager.MetadataKeyEnv: map[string]string{"MATCH_USER_IDS": strings.Join(userIds, ",")}, } if _, err := fm.Create(ctx, len(userIds), userIds, nil, metadata, callback); err != nil { return "", err } return "", nil})On the client, listen for the gameye_match notification and connect to host and port. The package README covers error handling (quota, capacity and timeout), per-player notification extras and the full example module.
Warm pools and env
Section titled “Warm pools and env”metadata["gameye.env"] is passed to the game server container as environment variables, and is never sent as session labels or written to Nakama storage. A session served from a warm pool runs in a container that started before the request, so it does not receive this env. Turn off the warm pool for the application and region if your server depends on per-match env. Gameye may also echo container env when sessions are read, so pass short-lived, per-match values rather than long-lived secrets.
Using with Unity Matchmaker
Section titled “Using with Unity Matchmaker”If you’re using Unity Matchmaker alongside Nakama, allocations can be routed to Gameye without changing your matchmaking rules or ticket logic. For a dedicated Unity Matchmaker integration that does not require Nakama, see the Unity Matchmaker guide.
Known limitations
Section titled “Known limitations”- Limited session list querying. Gameye’s Session API does not paginate, so
FleetManager#Listignores the query and cursor and returns every session for the configured region, application and tag. - No latency-based placement. Sessions start in the configured region;
Createignores player latencies. - No built-in retry queue. Sessions are created immediately on request. If session creation fails, retry logic must be handled at the application layer.
- No backfill helper.
Joinregisters more players with a running session, but finding a session with free seats is up to you.
Further reading
Section titled “Further reading”- Nakama Fleet Manager on GitHub — source, example module, changelog
- Gameye + Nakama integration overview — architecture and FAQ
- Core Concepts: Sessions and TTLs
- Region Selection guide
- POST /session reference