04 — Lobbies & matchmaking¶
With an access token (chapter 03) you ask the lattice-director service to
place you in a session and hand back a server endpoint + a session token, which you then feed
to lattice_runner_connect.
The getting-started guide states the flow explicitly at the C level:
sequenceDiagram
participant App as Your C/C++ client
participant AU as auth
participant DI as director
participant GS as Game server (your sim)
App->>AU: login → access_token
App->>DI: matchmake (Bearer access_token) → session_handle
App->>DI: resolve → { endpoint, session_token }
App->>GS: lattice_runner_connect(endpoint, session_token)
Verified against: docs-site/docs/getting-started/common-setup.md:43-52.
Read this first — the core has no matchmaking client
Just like auth, the director endpoints are plain HTTP you call yourself. lattice.h exposes
no /matchmake or /resolve function — the netcode ABI stops at lattice_runner_connect. The
director's endpoints are fully implemented server-side and return real data, but the glue
(making the HTTP calls, splitting the endpoint, enforcing the token server-side) is yours to
write. The exact status is at the end of this chapter.
Verified against: absence of any matchmake/resolve/director symbol in
reference/include/lattice/lattice.h; docs-site/docs/getting-started/common-setup.md:39-41.
The director runs on port 3010 in local dev. The /matchmake and /resolve request/response
shapes, their status codes, and the session-token claims are documented and verified per-line in the
Unity tutorial chapter 04 — they are the same HTTP service
regardless of client language, so those shapes apply to you unchanged. The rest of this chapter
covers the part that is specific to a C/C++ integrator: turning the resolve result into a
lattice_runner_connect call, and the honest wiring status.
Verified against: director port 3010 at run-all.sh:30 (PORT_DIRECTOR=3010); the endpoint
contracts at Unity chapter 04.
The two director calls (summary)¶
Both require the auth Bearer token from chapter 03.
POST /matchmakeplaces you in a session and returns asession_handle, a shortroom_codeand anendpoint(host:port).POST /resolveexchanges either identifier for concrete connect details and mints a session token. Resolving byroom_codealso needs your game'sX-Lattice-Api-Key, because short codes are unique within a game rather than globally:
{
"endpoint": "1.2.3.4:9000",
"session_token": "eyJ…", // director-signed Ed25519 JWT — pass to connect
"session_id": "b1f2…"
}
Verified against: the /matchmake + /resolve contracts documented in
Unity chapter 04 (grounded there against
control-plane/lattice-director/src/LatticeDirector/Program.cs and …/Api/Contracts.cs;
control-plane/lattice-director/src/LatticeDirector/Program.cs confirmed present in the repo).
Feeding /resolve into lattice_runner_connect¶
Here is the C-specific work. lattice_runner_connect takes a host (const char*), a uint16_t
port, and a token as bytes + length — so you split the endpoint string on : and pass the
session token as raw bytes:
lattice_result lattice_runner_connect(lattice_runner* r, const char* addr, uint16_t port,
const uint8_t* token, uint32_t token_len);
Verified against: reference/include/lattice/lattice.h:374-375.
// resolve returned: std::string endpoint (e.g. "1.2.3.4:9000"), std::string sessionToken (a JWT)
auto colon = endpoint.rfind(':');
std::string host = endpoint.substr(0, colon);
uint16_t port = (uint16_t)std::stoi(endpoint.substr(colon + 1));
lattice_runner_start(r, LATTICE_MODE_CLIENT);
lattice_runner_connect(r, host.c_str(), port,
(const uint8_t*)sessionToken.data(),
(uint32_t)sessionToken.size());
Verified against: reference/include/lattice/lattice.h:374-375; the "split the host:port
endpoint and pass the token bytes" pattern is the same one the Unity binding uses
(Unity chapter 04).
The endpoint is host:port as a string; there is no parser in the core
The core takes host and port separately — it does not parse a combined host:port. Split it
yourself (the sample above splits on the last : so IPv6-less hosts and explicit ports work).
Verified against: reference/include/lattice/lattice.h:374 (separate addr + port
parameters).
Where game servers come from¶
The director does not spawn servers. A session's endpoint is the address a running
game-server instance advertised by registering itself with the fleet. In the self-hosted path, you
run the server — a LATTICE_MODE_SERVER runner that lattice_runner_listens on a port (exactly
chapter 02's persistent_server.cpp) — and register its endpoint with the
director. With no live registered instance matching the requested region+mode, /matchmake returns
409.
Verified against: reference/tests/persistent_server.cpp:43-55 (a real SERVER + listen you run
yourself); the fleet-registration + 409 behaviour documented in
Unity chapter 04.
Verified wiring status (honest)¶
What is real:
- The connect target is real.
lattice_runner_connect(addr, port, token, token_len)accepts thehost,port, andsession_tokenbytes you obtained. This is the verified boundary between the control plane and the netcode.
Verified against:reference/include/lattice/lattice.h:374-375. - The director endpoints are real and return real data (a
session_handle, anendpoint, and a genuine Ed25519-signedsession_token), documented and per-line-verified in Unity chapter 04;control-plane/lattice-director/is present in the repo.
The caveats you must design around:
- The core includes no matchmaking client.
lattice.hhas no/matchmakeor/resolvefunction — you make those HTTP calls yourself (your TLS HTTP stack, or the plaintext-only mediated fetch from chapter 03), parse the JSON, split the endpoint, and pass the token bytes tolattice_runner_connect.
Verified against: absence of any matchmaking symbol inreference/include/lattice/lattice.h. - You must run and register a game server. The director never launches servers; you start your
own (
LATTICE_MODE_SERVER+lattice_runner_listen) and register its endpoint. With no live instance for the requested region+mode,/matchmakereturns 409.
Verified against:reference/tests/persistent_server.cpp:43-55; the 409 behaviour in Unity chapter 04. - Session-token enforcement at the game server is yours to wire. The
/resolvetoken is designed to be verified offline by the game server at the netcode handshake, but the reference skeleton omits crypto — the loopback/UDP handshake accepts an arbitrary token (the tests use"auth"). Verifying the director-signed token at the server is a responsibility of a production core / your integration, not something the reference proves.
Verified against:reference/include/lattice/lattice.h:12-14(the skeleton "deliberately omits crypto");reference/tests/two_process.cpp:251-252(arbitrary"auth"token accepted).
Bottom line: the matchmake → resolve → connect contract is real and returns real values; the glue (calling the director, splitting the endpoint, and enforcing the token server-side) is yours. For learning and for local/P2P play, skip the director entirely and use the listen-server flow from chapter 02 — the path the tests verify end to end.
Where the session token actually gets checked
Passing the token to lattice_runner_connect only delivers it. The game server verifies it inside the
handshake, through the token-validation seam of the
secure datagram channel — which is also what turns that
channel from unauthenticated into authenticated.
Next: 05 — Networked objects, the heart of replication.