Lobbies and Matchmaking

Lobby Basics

Lobbies are the matchmaking layer of the Photon Cloud. Every visible room is listed in exactly one lobby, and every matchmaking operation, room lists, random joins, filters, runs against the lobby you address. A lobby only organizes rooms; players in a lobby do not see or interact with each other.

The client automatically joins the default lobby when the connection is established, so simple games never need an explicit lobby call. Call SetAutoJoinLobby(false) before connecting to opt out, for example when the game always targets a named lobby anyway.

Lobby Types

LobbyType::Default matches rooms by exact equality on their lobby-visible custom properties. LobbyType::SqlLobby replaces equality matching with query-style filters over indexed properties and has its own page: SQL Lobby Matchmaking.

Joining and Leaving a Lobby

JoinLobby(name, type) enters a lobby and LeaveLobby() exits it; both return Task<Result<void>>. Calling JoinLobby() without arguments targets the default lobby, and IsInLobby() reports the current membership.

Joining a lobby the client is already in fails instead of being a no-op. Because the client auto-joins the default lobby on connect, switching to a named lobby right after connecting means leaving the default lobby first.

Switch from the default lobby to a named one:

C++
C

C++

Task<Result<void>> EnterRankedLobby(RealtimeClient& client)
{
    Result<void> left = co_await client.LeaveLobby();
    if (left.IsErr())
    {
        co_return left;
    }

    co_return co_await client.JoinLobby(PHOTON_STR("ranked"), LobbyType::Default);
}

C

/* Leave first, then join from the LeaveLobbyResult handler. */
realtime_leave_lobby(client);

if (e->type == RT_LeaveLobbyResult && e->mode == 0)
{
    realtime_join_lobby(client, "ranked", 0); /* lobbyType 0 = Default */
}

/* lobbyType takes the numeric LobbyType, and only 0, 2 and 3 are valid, so
   any other value falls back to Default.
   realtime_set_auto_join_lobby(client, 0) before connecting opts out of
   the automatic default-lobby join, which removes the need to leave it
   first. */

Room Lists

While the client is in a lobby the server pushes listing updates through OnRoomListUpdated(const std::vector<RoomListing>&). GetCachedRoomList() returns the most recent list without a round trip, which is usually all a room browser needs between updates.

Field Type Description
Name StringType Unique room name.
PlayerCount int Players in the room.
MaxPlayers uint8_t Player limit, 0 when unlimited.
IsOpen bool Whether the room accepts joins.
DirectMessaging DirectMode The room's direct messaging mode.
CustomProperties RealtimeMap The room's lobby-visible custom properties.

Random Matchmaking

JoinRandomRoom(MatchmakingOptions) asks the server to drop the player into a room matching the given options. When no room matches, the Task resolves to ErrorCode::NoMatchFound instead of waiting for one to appear.

JoinRandomOrCreateRoom(createOptions, matchmakingOptions) closes the classic race between "no match found" and "create a room": when nothing matches, the server creates the room described by createOptions in the same operation. This makes it the recommended default flow for drop-in multiplayer.

Match on a game mode and cap the room at eight players:

C++
C

C++

Task<Result<MutableRoomView>> EnterDeathmatch(RealtimeClient& client)
{
    MatchmakingOptions matchmaking;
    matchmaking.Filter[PHOTON_STR("mode")] = PHOTON_STR("deathmatch");
    matchmaking.MaxPlayers = 8;

    CreateRoomOptions create;
    create.MaxPlayers = 8;
    create.CustomProperties[PHOTON_STR("mode")] = PHOTON_STR("deathmatch");
    create.LobbyProperties = {PHOTON_STR("mode")};

    co_return co_await client.JoinRandomOrCreateRoom(create, matchmaking);
}

C

/* Search: the filter travels as a SQL filter string, and the lobby to match in
   is a parameter of the search itself. */
realtime_join_random_room(client,
                          8,          /* maxPlayers                       */
                          0,          /* matchmakingMode: FillRoom        */
                          NULL,       /* lobbyName: the default lobby     */
                          0,          /* lobbyType: Default               */
                          NULL);      /* sqlFilter: SQL lobbies only      */

/* Fall back to creating a matching room when nothing was found. */
#define RT_ERROR_NO_MATCH_FOUND 24

if (e->type == RT_JoinRandomRoomResult && e->mode == RT_ERROR_NO_MATCH_FOUND)
{
    rt_blob props = {0};
    rt_blob keys  = {0};

    rt_begin(&props);
    rt_put_str(&props, "mode", "deathmatch");
    rt_end(&props);

    realtime_create_room(client, NULL, 8, 1, 1, 0, 0, NULL, props.data, props.len);
    rt_free(&props);

    /* LobbyProperties are applied after the join, once in the room. */
    rt_begin(&keys);
    rt_add_str(&keys, "mode");
    rt_end(&keys);

    /* ... in the CreateRoomResult handler: */
    realtime_room_set_lobby_properties(client, keys.data, keys.len);
    rt_free(&keys);
}

/* Matchmaking Options Are Partly Available: realtime_join_random_room
   covers MaxPlayers, Mode, LobbyName, Lobby and SqlFilter, but not the
   equality Filter map or ExpectedUsers. Exact-equality matching on
   lobby-visible properties is therefore only reachable from C++; from C,
   express the same intent as a SqlFilter against a SQL lobby.

   realtime_join_random_or_create_room takes no matchmaking parameters at
   all - only the room it would create - so a filtered search plus fallback
   has to be written as the two-step flow above. */

Matchmaking Options

Field Default Description
Filter empty Custom properties a room must match exactly.
MaxPlayers 0 Only match rooms created with this player limit; 0 matches any.
Mode MatchmakingMode::FillRoom The fill strategy.
LobbyName empty The lobby to match in; empty targets the default lobby.
Lobby LobbyType::Default The type of the addressed lobby.
SqlFilter empty Query filter for the SQL lobby.
ExpectedUsers empty User ids to reserve slots for in the matched room.

Filter compares exactly: a room matches only when every property in the filter equals the room's lobby-visible custom property of the same key. Range or combination queries are not expressible here, that is what the SQL lobby is for.

MatchmakingMode picks the fill strategy. FillRoom (the default) fills rooms one after another, which gets players into full matches fastest; SerialMatching distributes players over the matching rooms in order and RandomMatching spreads them randomly, which keeps room populations even at high player counts.

Lobby Statistics

GetLobbyStats() returns a Task<Result<std::vector<LobbyStats>>> with the current player and room counts of each lobby on demand. Each LobbyStats entry carries the lobby Name, its Type, the PeerCount and the RoomCount.

For a continuously updated picture, construct the client with ClientConstructOptions.AutoLobbyStats = true (default false). The server then pushes statistics updates through OnLobbyStats; this push is independent of lobby membership and of the SetAutoJoinLobby setting.

Back to top