Matchmaking & Rooms
Last updated: 15 September 2026
A room is a live instance of your game, hosted by a game server and identified by a name. From a Unity client you get a player into a room in one of two ways: browse the list of open rooms and let the player pick, or join a room by name directly. Either way you receive a reservation — a short-lived ticket plus the server's connect details — which you use to open your game transport.
This is the Unity client side: discovering and reserving a room. Running the game server that hosts rooms is a separate, server-side concern. Rooms do not require automatic matchmaking — browsing and join-by-name work on their own. Automatic placement is a separate layer, covered at the end, and is not in service yet.
Browse open rooms
BrowseRoomsAsync reads the public room list for a game-server function, using the signed-in player's session. You get a page of rooms; the player chooses one.
using PlayServ.Sdk.Matchmaking;
var page = await PlayServMatchmaking.BrowseRoomsAsync("arena");
foreach (PlayServRoomListing room in page.Rooms)
{
// room.RoomName, room.Players / room.Capacity, room.State,
// room.Region, room.Connect (host/port/transport)
}
// Paging is explicit — request the next page only if you want it:
if (page.HasMore)
page = await PlayServMatchmaking.BrowseRoomsAsync("arena",
new PlayServRoomBrowseQuery { Cursor = page.CursorNext });
A PlayServRoomBrowseQuery can filter by PlacementState (e.g. only joinable rooms) and Region, set a Limit (default 50), and carry a Cursor for the next page. Browsing never places or launches anything — it's a read.
Join a room by name
Once the player has picked a room — or if your game already knows the room name (a private match, a friend's room, a room from your own server browser) — JoinRoomAsync requests a reservation for exactly that room.
PlayServMatchResult result = await PlayServMatchmaking.JoinRoomAsync("arena", roomName);
if (result.IsMatched)
{
PlayServMatchReservation r = result.Reservation;
// r.RoomName, r.ReservationToken, r.ExpiresAt
// r.Connect.Host, r.Connect.Port, r.Connect.Transport, r.Connect.ConnectString, r.Connect.Region
}
JoinRoomAsync does one thing: it asks for a reservation. It does not place, launch, poll, retry, or open a transport — that keeps it predictable. If you ask twice for the same room while a reservation is still active, you get the same one back rather than a duplicate.
When a join can't be satisfied, the result carries the reason — the room doesn't exist (room_not_found), is full (room_full), or is closed/draining (room_closed). Handle those by sending the player back to the browser.
The reservation
Both paths end in a PlayServMatchReservation, which is everything you need to reach the room:
| Field | What it is |
|---|---|
RoomName | The room the reservation admits you to. |
ReservationToken | An opaque, single-use token, bound to your session. Present it to the game server when you connect. |
ExpiresAt | When the reservation lapses — connect promptly. |
Connect | Where the server is: Host, Port, Transport, plus an optional ConnectString and Region. |
The reservation is your permission to enter one specific room. It expires, and it's single-use, so redeem it soon after you get it.
Join and connect in one call
JoinRoomAsync deliberately doesn't touch your networking, because PlayServ doesn't own your game transport (Netcode for GameObjects, Unity Transport, Mirror, a raw socket — your choice). When you'd rather not wire the two steps together yourself, JoinRoomAndConnectAsync reserves the room and then hands the reservation to a connector you supply, where you open your transport to Connect and present the ReservationToken.
await PlayServMatchmaking.JoinRoomAndConnectAsync(
new PlayServJoinRoomRequest { FunctionSlug = "arena", RoomName = roomName },
connector: async (reservation, ct) =>
{
// open YOUR transport to reservation.Connect and hand it the token
});
The connector must run on Unity's main (synchronization) context, since it typically touches Unity networking objects. Everything the connector needs — address and token — is on the reservation.
For the whole path from sign-in to in-room, including how the reservation and connect details fit together, see Connecting to a Game.
Talking to other players in the room
Getting into a room is separate from messaging inside it. For lobby chat, ready-checks and "who's here" signalling, use Groups: subscribe to a group keyed on the room and publish events to everyone in it. Matchmaking gets the player to the room; groups let the room talk.
Automatic matchmaking
The SDK also exposes automatic placement — FindMatchAsync and JoinGameAsync, which pick a room for the player instead of having them browse or name one. The platform has no matchmaking in service today, so these have nothing behind them. Build on browse + join-by-name (above), which is fully supported. This section documents the intended shape only.
When it ships, automatic matchmaking will let a player request a match and be placed into a suitable room (starting one if needed), returning the same kind of reservation you already handle:
// NOT IN SERVICE YET
var result = await PlayServMatchmaking.JoinGameAsync(
new PlayServJoinGameRequest { FunctionSlug = "arena" });
// result.Status: Matched · Bot · NotFound → result.Reservation as above
Because the outcome is the same reservation type, code that already browses and joins by name will carry over with little change once placement is live.
Next steps
- Connecting to a Game — the full Unity path: sign in → reserve → connect → play
- Groups — in-room chat, presence signalling and broadcast
- Game Server Hosting — the server that hosts the rooms you browse and connect to