Matchmaking

Matchmaking With the Servers Included.

Submit tickets and let us form the match, or bring the matchmaker you already run. Either way, one call claims capacity on a warm game server, delivers the roster to it, and hands you an IP and port.

# Your backend: queue a party of two for an 8 player match
POST /v1/apps/dscp_.../tickets
Authorization: Bearer dsc_...

{
  "ticketId": "party-77",
  "queue": "ranked",
  "sessionSize": 8,
  "partySize": 2,
  "filters": { "meta": { "location": "ams" } }
}

# Poll until the match forms, then send the party to the server
GET /v1/apps/dscp_.../tickets/party-77

{
  "error": false,
  "status": "matched",
  "serverId": "4211",
  "ip": "203.0.113.9",
  "port": 27015
}
2 sec
Match Loop
60 sec
Claim Auto-Release
10 min
Idempotent Retries
Agones
Compatible SDK

Most Matchmakers Stop at the Match

Forming a fair match is the part everyone solves. Getting those players onto a game server that has room, and telling it what to run, is the part that is left to you.

The Gap

A Match Is Not a Server

Queue logic hands you eight players and a lobby id. Something still has to find a machine with room, hold that room while the players connect, and tell it who is coming.

One Primitive

Allocation Does All Three

A single call claims capacity on one live game server, delivers your match context to it, and returns the address. There is no server list to read and race against.

Bring Your Own

Or Keep the Matchmaker You Have

The built-in ticket matchmaker calls the same public allocation API you would. A director or custom lobby service integrates the same way, with no private capabilities held back.

Built-in ticketsOpen Match directorCustom lobby serviceParty serviceAny backend

From Queue to Connection

Three steps, and only the first one is yours to choose.

01

Queue or Form the Match

Submit tickets with a queue name, session size, and party size and let the service form matches. Or form them yourself, in your own backend, from your own queue.

02

Allocate a Game Server

One call claims counter capacity on an eligible game server and pushes the match context to it. A claim nobody confirms releases itself, so a crashed handoff costs you nothing.

03

Connect the Players

You receive an IP and port. The game server already has the roster, so the match starts without a second round trip through your backend.

What matchmaking allocates

Matches are placed on fleet game servers, which are deployments you run on PingCore and which connect to Discovery on their own. Game servers you host elsewhere can still appear on the public server list through heartbeats, but they are never allocated.

Allocation Is a Transaction, Not a Lookup

Reading a list of game servers and picking one is a race. Claiming capacity is not. The claim either succeeds on a game server that genuinely had room, or it tells you nothing was free.

  • Claims capacity atomically, so two matchmakers cannot double-book one slot
  • Replays the original allocation whenever a retry reuses the same idempotency key
  • Releases unconfirmed claims automatically after 60 seconds
  • Restricts candidates by exact game version and your own server metadata
  • Places a match in one location with a single metadata filter
  • Packs best-fit, so busy game servers fill and idle ones drain for scale-down
POST /v1/apps/dscp_.../allocations
Authorization: Bearer dsc_...

{
  "idempotencyKey": "match-83a1",
  "claims": { "sessions": 1 },
  "filters": { "meta": { "location": "ams" } },
  "context": { "mode": "ranked", "roster": ["p1", "p2"] }
}

{
  "error": false,
  "allocationId": "...",
  "serverId": "4211",
  "status": "pending",
  "ip": "203.0.113.9",
  "port": 27015,
  "expiresIn": 60,
  "replayed": false
}

A 409 with no_capacity means nothing eligible is free right now. Back off and retry with the same key: a repeat answers with replayed: true and the original allocation, so a retry can never double-allocate.

The Fleet Grows With the Queue

Capacity is the other half of matchmaking. Each location is reconciled once a minute against live state, inside the floor and ceiling you set.

BufferFleet setting

Keep Game Servers Warm

Each location holds a number of idle, ready game servers so a match never waits on a cold start. The autoscaler tops the buffer back up as matches consume it.

HintOne call per tick

Signal Demand Early

When your matchmaker can see a queue building or an event starting, write the number of warm game servers you want. The autoscaler splits it across the fleet deployments and sizes up ahead of the load.

FloorNo code

Never Interrupt a Match

Scale-down only removes idle game servers, re-checked against live state at removal time. A location never drains to zero, and each cycle is capped so a bad hint cannot empty a fleet.

PUT /v1/apps/dscp_.../desired-capacity

{ "desiredReadyServers": 12 }

The hint expires on its own, so a quiet matchmaker lets every location fall back to its configured buffer instead of pinning stale demand forever. The built-in ticket matchmaker maintains it for you.

The Game Server Already Knows About the Match

Whatever you attach to an allocation reaches the game server before the players do. The roster, the mode, the map, the tournament id. Read it, run the match, end the session.

  • An Agones-compatible SDK on localhost, so games already written for Agones work unmodified
  • Lightweight native session endpoints when you would rather not adopt an SDK
  • RCON occupancy polling for live player counts with no game changes at all
  • A session that ends returns the game server to ready for the next match
// Delivered to the allocated game server
{
  "matchmaker": true,
  "queue": "ranked",
  "sessionSize": 8,
  "roster": [
    {
      "ticketId": "party-77",
      "partySize": 2,
      "context": { "partyName": "the crew" }
    },
    {
      "ticketId": "solo-4",
      "partySize": 1,
      "context": null
    }
  ]
}

Matches you allocate yourself receive exactly the context you posted. Branch on matchmaker to tell the two apart.

Discovery

Matchmaking Rides on Discovery

Matchmaking is a layer on the service that already runs your public server list. A fleet gives you the listing and the live state with no code, and allocations sit on top when your game needs sessions.

  • A fleet connects your deployments to Discovery automatically
  • The public server list works with or without matchmaking
  • Live lifecycle state comes from the same app your matchmaker allocates against
  • One app, with separate tokens issued per scope
Explore Discovery
Dedicated game servers
POST /v1/heartbeat every 30 seconds
Discovery
Live state expires after 90 seconds
Game clients
GET /v1/apps/{publicId}/servers

The game client receives a public ID. Heartbeat tokens remain on the dedicated game servers.

Allocation Is a Backend Capability

Every endpoint on this page belongs in your matchmaker, not in your game client. Token scopes make that boundary something the service enforces rather than something you remember.

Allocate scope

Your Matchmaker

Allocations, tickets, and the demand hint. Issue it to the backend that forms matches and nowhere else.

Heartbeat scope

Ingest and Reads

Live state and heartbeat ingest only. A heartbeat token asking to allocate is refused, not quietly allowed.

Scopes are set per token

Issue separate tokens for your matchmaker and your fleet. Rotating or revoking one never stops the other, and a revocation takes effect within about a minute.

Put Your Next Match on a Warm Server

Create a fleet, issue an allocate token, and place your first match. Start with the built-in ticket matchmaker and swap in your own whenever you outgrow it.