> ## Documentation Index
> Fetch the complete documentation index at: https://docs.playflowcloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Lobby Manager API Reference

> A detailed API reference for the PlayFlowLobbyManagerV2 class in the Unity SDK.

This document provides a detailed reference for the public properties and methods of the `PlayFlowLobbyManagerV2` class.

## Properties

<ResponseField name="Instance" type="PlayFlowLobbyManagerV2" required>
  Singleton instance of the Lobby Manager. Access all lobby functionality through this.
</ResponseField>

<ResponseField name="IsReady" type="bool" required>
  Returns true if the manager has been successfully initialized.
</ResponseField>

<ResponseField name="CurrentLobby" type="Lobby" required>
  Gets the full data for the lobby the player is currently in. Returns null if not in a lobby.
</ResponseField>

<ResponseField name="PlayerId" type="string" required>
  Gets the unique ID of the current player, as provided during initialization.
</ResponseField>

<ResponseField name="State" type="LobbyState" required>
  Gets the current state of the lobby session (e.g., Disconnected, InLobby).
</ResponseField>

<ResponseField name="Events" type="PlayFlowEvents" required>
  Provides access to all lobby-related UnityEvents (e.g., OnLobbyJoined, OnPlayerLeft).
</ResponseField>

<ResponseField name="AvailableLobbies" type="List<Lobby>" required>
  A cached list of all currently available public lobbies. This list is updated automatically if auto-refresh is enabled.
</ResponseField>

<ResponseField name="IsInLobby" type="bool" required>
  A quick check to see if the player is currently in a lobby.
</ResponseField>

<ResponseField name="IsHost" type="bool" required>
  Returns true if the current player is the host of the lobby they are in.
</ResponseField>

<ResponseField name="InviteCode" type="string" required>
  Gets the invite code for the current private lobby. Returns null if not in a private lobby.
</ResponseField>

## Methods

<AccordionGroup>
  <Accordion title="Initialize">
    Initializes the lobby manager. This must be called before any other lobby operations.

    ```csharp theme={null}
    public void Initialize(string playerId, Action onComplete = null)
    ```

    <ResponseField name="playerId" type="string" required>
      A unique identifier for the local player.
    </ResponseField>

    <ResponseField name="onComplete" type="Action">
      An optional callback that is invoked when initialization is successful.
    </ResponseField>
  </Accordion>

  <Accordion title="CreateLobby">
    Creates a new lobby. There are two overloads for this method.

    Pass `forceFresh: true` to replace any lobby the local player currently **hosts**
    in this config — the backend deletes the old lobby (stopping its game server
    best-effort) before creating the new one. Intended for reconnect / "start a
    fresh session" flows, where the host may still own a stale lobby from a
    previous run. It's a no-op when the player has no prior lobby.

    **Simple:**

    ```csharp theme={null}
    public void CreateLobby(string name, int maxPlayers = 4, bool isPrivate = false, bool forceFresh = false, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="name" type="string" required>The public name of the lobby.</ResponseField>
    <ResponseField name="maxPlayers" type="int">The maximum number of players (1–100). The SDK sends `4` when omitted; if you call the API directly and omit this field, the server default is `2`.</ResponseField>
    <ResponseField name="isPrivate" type="bool">If true, the lobby will not appear in public searches.</ResponseField>
    <ResponseField name="forceFresh" type="bool">If true, delete any lobby the local player already hosts before creating a new one. Defaults to `false`.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the created lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>

    **Detailed:**

    ```csharp theme={null}
    public void CreateLobby(string name, int maxPlayers, bool isPrivate, bool allowLateJoin, string region, Dictionary<string, object> customSettings, bool forceFresh = false, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="name" type="string" required>The public name of the lobby.</ResponseField>
    <ResponseField name="maxPlayers" type="int" required>The maximum number of players that can join.</ResponseField>
    <ResponseField name="isPrivate" type="bool" required>If true, the lobby will not appear in public searches.</ResponseField>
    <ResponseField name="allowLateJoin" type="bool" required>If true, players can join even after the match has started.</ResponseField>
    <ResponseField name="region" type="string" required>The server region for the lobby (e.g., "us-west").</ResponseField>
    <ResponseField name="customSettings" type="Dictionary<string, object>" required>A dictionary of custom game-specific settings.</ResponseField>
    <ResponseField name="forceFresh" type="bool">If true, delete any lobby the local player already hosts before creating a new one. Defaults to `false`.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the created lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="CreateFreshLobby">
    Shorthand for `CreateLobby(..., forceFresh: true)`. The backend first deletes
    any lobby this player already hosts (stopping its game server best-effort),
    then creates a brand-new lobby atomically — replacing the old
    "TryReconnect → DeleteLobby → CreateLobby" dance with a single call.

    ```csharp theme={null}
    public void CreateFreshLobby(string name, int maxPlayers = 4, bool isPrivate = false, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="name" type="string" required>The public name of the lobby.</ResponseField>
    <ResponseField name="maxPlayers" type="int">The maximum number of players (1–100). The SDK sends `4` when omitted; the raw API default is `2`.</ResponseField>
    <ResponseField name="isPrivate" type="bool">If true, the lobby will not appear in public searches.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the created lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="JoinLobby">
    Joins an existing lobby using its unique ID.

    ```csharp theme={null}
    public void JoinLobby(string lobbyId, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="lobbyId" type="string" required>The unique ID (UUID) of the lobby to join.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the joined lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="JoinLobbyByCode">
    Joins an existing private lobby using its invite code.

    ```csharp theme={null}
    public void JoinLobbyByCode(string inviteCode, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="inviteCode" type="string" required>The invite code of the lobby to join.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the joined lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="GetAvailableLobbies">
    Fetches the list of public lobbies in the project. Results are also cached on the `AvailableLobbies` property.

    ```csharp theme={null}
    public void GetAvailableLobbies(Action<List<Lobby>> onSuccess, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<List<Lobby>>" required>Callback invoked with the list of public lobbies.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="LeaveLobby">
    Leaves the lobby the player is currently in.

    ```csharp theme={null}
    public void LeaveLobby(Action onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action">Callback invoked on successfully leaving the lobby.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="UpdatePlayerState">
    Updates the **local player's** custom state data within the lobby. This is useful for synchronizing data like character selection or ready status.

    ```csharp theme={null}
    public void UpdatePlayerState(Dictionary<string, object> state, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="state" type="Dictionary<string, object>" required>A dictionary representing the player's custom data.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="UpdateStateForPlayer">
    Kept for backward compatibility. In V3, **only the target player can update their own state** — if `targetPlayerId` isn't the local player this call routes to `UpdatePlayerState()` and logs a warning. For team/role assignment, have each player set their own team via `UpdatePlayerState()` or push assignments through `UpdateLobbySettings()` (host-owned).

    ```csharp theme={null}
    public void UpdateStateForPlayer(string targetPlayerId, Dictionary<string, object> state, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="targetPlayerId" type="string" required>The unique ID of the player whose state you want to update. Must equal the local player ID.</ResponseField>
    <ResponseField name="state" type="Dictionary<string, object>" required>A dictionary representing the new data for the target player.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="KickPlayer">
    Kicks a player from the lobby. This can only be called by the host.

    ```csharp theme={null}
    public void KickPlayer(string playerToKickId, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="playerToKickId" type="string" required>The unique ID of the player to remove from the lobby.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="UpdateLobby">
    Host-only. Updates lobby metadata and settings. Pass only the fields you want to change — unspecified fields are left untouched. Cannot be called while the lobby is `in_game`.

    ```csharp theme={null}
    public void UpdateLobby(
        string name = null,
        int? maxPlayers = null,
        bool? isPrivate = null,
        bool? allowLateJoin = null,
        string region = null,
        Dictionary<string, object> customSettings = null,
        Action<Lobby> onSuccess = null,
        Action<string> onError = null)
    ```

    <ResponseField name="name" type="string">New lobby name (1–100 characters).</ResponseField>
    <ResponseField name="maxPlayers" type="int?">New max player count (1–100).</ResponseField>
    <ResponseField name="isPrivate" type="bool?">Toggle public/private listing.</ResponseField>
    <ResponseField name="allowLateJoin" type="bool?">Allow players to join after the match starts.</ResponseField>
    <ResponseField name="region" type="string">Region for the next game server (e.g. `"us-west"`).</ResponseField>
    <ResponseField name="customSettings" type="Dictionary<string, object>">Replace the custom settings blob.</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>

    <Info>
      Invite codes can't be toggled per-lobby at runtime. Whether a lobby type issues invite codes is controlled by its `inviteCodeConfig` in Project Settings; every lobby of a type that has it enabled gets a code automatically.
    </Info>
  </Accordion>

  <Accordion title="UpdateLobbySettings">
    Convenience shortcut for `UpdateLobby(customSettings: newSettings)`. Host-only. Use this when you only need to update the `customSettings` dictionary.

    ```csharp theme={null}
    public void UpdateLobbySettings(Dictionary<string, object> newSettings, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="newSettings" type="Dictionary<string, object>" required>New custom settings dictionary (replaces the existing one).</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="DeleteLobby">
    Host-only. Deletes the current lobby for every player. All members are kicked and the lobby stops appearing in public searches.

    ```csharp theme={null}
    public void DeleteLobby(Action onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action">Callback invoked once the lobby has been deleted.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="TransferHost (V3: not supported)">
    <Warning>
      V3 does not support explicit host transfer. Calling this method logs a warning and invokes `onError`. When the current host leaves, the server promotes a new host automatically — no manual transfer needed. Kept on the class for backward compatibility with V2 integrations.
    </Warning>

    ```csharp theme={null}
    public void TransferHost(string newHostId, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```
  </Accordion>

  <Accordion title="StartMatch">
    Starts the match directly, skipping matchmaking. Host-only. Launches a dedicated game server and transitions status to `in_game`. Use this for "Invite a friend" style lobbies; use `FindMatch()` for competitive queues.

    ```csharp theme={null}
    public void StartMatch(Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="EndMatch">
    Host-only. Rematch helper — stops the current game server, returns the lobby to `waiting` while keeping all players, invite code, and settings intact. Perfect for a "Play again" button. Requires lobby status `in_game`.

    If the server stops on its own (TTL, crash, clean exit), the lobby also auto-heals to `waiting` — no call needed.

    ```csharp theme={null}
    public void EndMatch(Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked with the updated lobby data on success.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  ### Matchmaking

  <Accordion title="FindMatch">
    Host-only. Enters the lobby into the matchmaking queue for the given mode. Modes are configured per-project in the dashboard (5v5, 1v1, FFA, party sizes, skill rules, etc.).

    Subscribe to `OnMatchmakingStarted`, `OnQueueStats`, and `OnMatchFound` / `OnMatchAwaitingConfirmation` to drive your matchmaking UI. The lobby transitions `waiting → in_queue → in_game` (or `match_found` first, if the mode has `matchConfirmation` enabled).

    ```csharp theme={null}
    public void FindMatch(string mode, Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="mode" type="string" required>Matchmaking mode name (must exist in dashboard config).</ResponseField>
    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked once the lobby enters the queue.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked on validation/API error.</ResponseField>
  </Accordion>

  <Accordion title="CancelMatchmaking">
    Host-only. Exits the matchmaking queue. Lobby returns to `waiting` with all players intact.

    ```csharp theme={null}
    public void CancelMatchmaking(Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked when back in `waiting`.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="ConfirmMatch">
    CS2-style "Accept Match". Only relevant when the matchmaking mode has `matchConfirmation.enabled: true` and the lobby is in `match_found` status. Any player in the lobby can call this. Once every matched lobby confirms, the game server launches and status becomes `in_game`.

    Listen for `OnMatchAwaitingConfirmation` (match pending), `OnMatchConfirmed` (you/your lobby accepted), and `OnMatchDeclined` (someone declined or the window timed out — back to `waiting`).

    ```csharp theme={null}
    public void ConfirmMatch(Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked once the confirmation is recorded.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  <Accordion title="DeclineMatch">
    Declines a found match. **Cancels the match for every participating lobby** — one dissenter bails everyone. All lobbies (yours included) return to `waiting`; players must explicitly re-queue if they want to try again. Only valid while the lobby is in `match_found`.

    ```csharp theme={null}
    public void DeclineMatch(Action<Lobby> onSuccess = null, Action<string> onError = null)
    ```

    <ResponseField name="onSuccess" type="Action<Lobby>">Callback invoked once the decline is recorded.</ResponseField>
    <ResponseField name="onError" type="Action<string>">Callback invoked with an error message on failure.</ResponseField>
  </Accordion>

  ### Connection Info

  <Accordion title="GetGameServerConnectionInfo">
    Convenience helper that returns the first port as a simple `{ Ip, Port }` tuple. Good for single-port games; for anything else use the named lookups below.

    ```csharp theme={null}
    public ConnectionInfo? GetGameServerConnectionInfo()
    ```

    **Returns:** A `ConnectionInfo` struct with `Ip` and `Port` of the first `network_ports` entry, or `null` if no server is running.
  </Accordion>

  <Accordion title="Port lookups (by name) — recommended">
    Look up a network port by the name you defined in your project's port configuration (e.g. `"game_udp"`, `"voice_tcp"`). These live directly on `Lobby` (and mirror to `GameServerInfo`) so you don't need to hand-code `server.network_ports[0]` and hope.

    ```csharp theme={null}
    // On Lobby (null-safe; returns null / 0 if no server or no match)
    string        host           = lobby.GetHost("game_udp");
    int           externalPort   = lobby.GetExternalPort("game_udp");
    GameServerPort port          = lobby.FindPort("game_udp");
    bool          ok             = lobby.TryGetPort("game_udp", out var p);
    ```

    The full `GameServerPort` shape matches the servers API: `name`, `internal_port` (what your game binds to inside the container), `external_port` (what clients connect to), `protocol` (`udp` / `tcp`), `host`, `tls_enabled`. Connect with `host:external_port`.

    <Tip>
      Define port names once in the dashboard under **Project Settings → Port Configuration** (e.g. `game_udp` for gameplay, `query_tcp` for server browser, `voice_tcp` for voice). Reference those names from Unity — you never need to hardcode port numbers.
    </Tip>
  </Accordion>
</AccordionGroup>
