A match is a group of lobbies sharing one server. Backfill adds more lobbies to that same
matchId and the same running server, up to the match’s capacity (teams × playersPerTeam).The engine-authoritative hybrid model
PlayFlow runs backfill as an engine-authoritative hybrid:- The engine owns backfill by default. When a lobby queues for a backfill-enabled mode, the matcher first looks for a compatible open match. If it finds one, the lobby joins that running server; if not, it forms a fresh match as usual. The engine tracks capacity, per-team slots, the skill band, and the backfill window — you don’t have to.
- Your game server admits players from an authoritative roster. The engine maintains the single source of truth for who belongs in the match. Your server polls
GET /v3/servers/{instance_id}/rosterand admits connecting players against it. This is what fixes reconnect / “denied by proxy” edge cases — the roster, not the server’s local memory, decides admission. - Advanced servers can optionally take control. A server that knows better when it can accept joiners (e.g., only between rounds) can open, close, or re-scope backfill itself via
PATCH /v3/servers/{instance_id}/backfillandPOST /v3/servers/{instance_id}/backfill/lock.
Enabling backfill
Backfill is configured per matchmaking mode in your lobby config. Add abackfill object to the mode:
backfill fields
| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | — | Master switch. When true, newly-queued lobbies can join an open match before a fresh server is launched. |
windowSeconds | integer ≥ 1 | 300 | How long after a match forms it stays open to joiners. After this window the match closes to backfill. |
closeWhenFull | boolean | true | Close backfill automatically once the match reaches capacity. |
skill | object | null | Optional skill gate. A joining lobby’s average value on field must be within maxDifference of the running match’s skill band. |
Deprecated: allowBackfill
The old boolean flag still works and is treated as a shorthand:
backfill object takes precedence if both are present. Prefer backfill for new configs — the dashboard drops allowBackfill on the next edit. Existing projects on allowBackfill: true keep working unchanged and now get real backfill (not just candidate discovery).
What happens when a lobby queues
For a backfill-enabled mode, the matcher tries to backfill before forming a fresh match:Search for an open match
The engine looks for a running match in the same project, lobby config, and mode with
status: in_game and backfill_open: true, whose backfill window hasn’t expired.Check compatibility
The candidate match must be region-compatible, have remaining capacity ≥ the joining lobby’s eligible players, and (if a skill gate is set) the lobby’s average skill must fall inside the match’s band.
Claim the slots
The engine atomically claims the open slots (assigning players to the teams with the largest deficit) and transitions the lobby to
in_game with the running match’s matchId and server connection info.Players connect to the running server
The joined lobby receives the same
server.network_ports[] as everyone already in the match — over the existing SSE stream — and connects immediately. No launch wait.windowSeconds (or until it fills, when closeWhenFull is true). Players who leave during a match free their slots, and the engine reopens backfill if the match is still under capacity and inside its window.
Server integration: admitting players
Your game server is the authority on gameplay, but the engine is the authority on the roster. Your server should admit connecting players against the roster it polls from the engine — not against whatever it happened to see at launch. This is the single change that makes backfill (and reliable reconnects) work.Poll the roster
pf_...) and use the server’s own instance ID. At launch the platform injects these environment variables into your server container — read them rather than hardcoding:
| Env var | Value |
|---|---|
PLAYFLOW_INSTANCE_ID | The server’s own instance_id (use it to build the roster URL). |
PLAYFLOW_API_KEY | The project server API key (pf_...). |
PLAYFLOW_API_URL | The API base (e.g. https://api.computeflow.cloud/api). |
PLAYFLOW_MATCH_ID | The match this server is hosting. |
status is one of in_game, locked, or completed. Keep admitting while it’s in_game; once it’s locked or completed, backfill is closed — stop admitting new joiners and treat the match as sealed.roster_version — cheap change detection
roster_version is a monotonically increasing counter bumped on every roster change (a backfill joiner admitted, a player released, a backfill state toggle). Store the last version you saw and only re-diff your admission list when it changes:
- Version unchanged → nothing to do.
- Version increased → a backfill joiner arrived (or a player left). Reconcile your local player list with
roster.
Admission rule
When a player connects (or reconnects), check whether theirplayerId is in roster:
- In the roster → admit them, and place them on their assigned
team. - Not in the roster → reject the connection.
- Unity (C#)
- Godot (GDScript)
Optional: server-authoritative control
Most games never need this — the engine opens and closes backfill for you based on the window and capacity. But if your server has better knowledge of when it can accept joiners (e.g., only between rounds, or once a boss phase starts), it can take control.Open or close backfill (and re-scope skill)
| Field | Type | Description |
|---|---|---|
accepting | boolean | true opens backfill; false closes it. |
skillBand | object | null | Optional. Replace the match’s skill band for joiners: { field, min, max }. Omit or send null to leave the current band unchanged. |
Close backfill (shorthand)
Close the match to further joiners — a convenience shorthand forPATCH ... { "accepting": false }:
ServerBackfillState shape as PATCH. Both bump roster_version, so a polling server sees the change on its next GET /roster.
Behavior reference
| Situation | Outcome |
|---|---|
| Lobby queues, compatible open match exists | Lobby joins the running server; no new server launches |
| No open match / lost the claim race | Falls through to fresh match formation |
Match fills and closeWhenFull is true | Backfill closes automatically |
Backfill window (windowSeconds) elapses | Match closes to joiners |
| Player leaves mid-match | Slot freed; backfill reopens if under capacity and inside the window |
Mode has no backfill / enabled: false | Backfill path never runs — behaves exactly as before |
Matchmaking
Modes, teams, skill rules, and how matches form.
Real-time Events
Stream the
in_game transition and server connection info via SSE.