---
title: "Create and join lobbies"
description: "Bring players together in a lobby and keep its members up to date."
seo:
  noindex: true
---

{/* Copyright (c) 2026 Betide Studio. All Rights Reserved. */}

[Sign in](/crossplay/identity) first. Choose the lobby provider and any mirrors in **Betide → Crossplay**. A lobby groups players; [hosting the match](/crossplay/listen-server) is a separate step.

## Create a lobby

**Blueprint**

<BlueprintViewer title="Create a lobby" src="/blueprints/crossplay-create-lobby.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void CreateParty(UGameInstance &GameInstance)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    FUCIK_LobbySettings Settings;
    Settings.MaxMembers = 4;
    Settings.JoinPolicy = EUCIK_JoinPolicy::Public;
    FUCIK_Attribute Mode;
    Mode.StringValue = TEXT("Coop");
    Settings.Attributes.Add(TEXT("Mode"), Mode);
    Service->CreateLobby(
        TEXT("GameLobby"), Settings,
        FBetideCallback<FUCIK_Lobby>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<FUCIK_Lobby> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                UE_LOG(LogUCIK, Log, TEXT("Lobby: %s"), *Result.GetValue().Name.ToString());
            }));
}
```

Use the same **Lobby Name** for later operations. It is a local slot, not a public lobby ID. Mirror creation can leave a usable primary lobby: check **Has Mirrors** and **Mirror Error** before promising cross-store discovery.

Discord can only mirror. Add `Betide.Discord` to **Lobbies → Mirrors** and every crossplay lobby also becomes a Discord lobby keyed by a secret UCIK generates; the secret rides the primary lobby's metadata so joiners follow it without a search. Discord lobbies cannot be listed or searched, so Discord is never the primary. Because the Discord client SDK cannot change lobby metadata after creation, UCIK stores the mirror's metadata in a marked lobby message; those messages never reach **On Lobby Message**. Discord reports no owner and no kick or promote, so host election and moderation stay with the primary provider.

## Find lobbies

**Blueprint**

<BlueprintViewer title="Find lobbies" src="/blueprints/crossplay-find-lobbies.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void FindCoopParties(UGameInstance &GameInstance)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    FUCIK_LobbySearch Search;
    Search.MaxResults = 20;
    Search.MinOpenSlots = 1;
    FUCIK_SearchFilter Filter;
    Filter.Key = TEXT("Mode");
    Filter.Value.StringValue = TEXT("Coop");
    Search.Filters.Add(Filter);
    Service->FindLobbies(
        Search, FBetideCallback<TArray<FUCIK_Lobby>>::CreateWeakLambda(
                    &GameInstance, [](const TBetideResult<TArray<FUCIK_Lobby>> &Result) {
                        if (!Result.bOk)
                        {
                            UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                            return;
                        }
                        UE_LOG(LogUCIK, Log, TEXT("Lobbies: %d"), Result.GetValue().Num());
                    }));
}
```

Filters are combined with AND. Attribute types must match the host's values. Keep the complete search result for joining. Search results carry a member count without the member list; show it with **Get Crossplay Lobby Member Count** and **Get Crossplay Lobby Open Slots** (sessions: **Get Crossplay Session Open Slots**), which report -1 when the provider gave no capacity.

## Join a lobby

**Blueprint**

<BlueprintViewer title="Join a lobby" src="/blueprints/crossplay-join-lobby.txt" />

**C++**

Game module dependencies: `UCIKCore`, `BetideCore`.

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

void JoinParty(UGameInstance &GameInstance, const FUCIK_Lobby &SelectedLobby)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Service)
        return;

    Service->JoinLobby(
        SelectedLobby, false,
        FBetideCallback<FUCIK_Lobby>::CreateWeakLambda(
            &GameInstance, [](const TBetideResult<FUCIK_Lobby> &Result) {
                if (!Result.bOk)
                {
                    UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                    return;
                }
                UE_LOG(LogUCIK, Log, TEXT("Joined: %s"), *Result.GetValue().Name.ToString());
            }));
}
```

Leave **Auto Travel** off for a waiting room. Use **On Lobby Game Connection Available** when the host starts the match; see [listen servers](/crossplay/listen-server).

## Manage the lobby

**Blueprint**

<Accordion>
<AccordionItem title="Expand a party">
<BlueprintViewer title="Expand a party" src="/blueprints/crossplay-update-lobby.txt" />
  </AccordionItem>

  <AccordionItem title="Read lobby members">
<BlueprintViewer title="Read lobby members" src="/blueprints/crossplay-query-members.txt" />
  </AccordionItem>

  <AccordionItem title="Set your ready state">
<BlueprintViewer title="Set your ready state" src="/blueprints/crossplay-lobby-ready.txt" />
  </AccordionItem>

  <AccordionItem title="Send lobby chat">
<BlueprintViewer title="Send lobby chat" src="/blueprints/crossplay-send-lobby-message.txt" />
  </AccordionItem>

  <AccordionItem title="Transfer ownership">
<BlueprintViewer title="Transfer ownership" src="/blueprints/crossplay-promote-member.txt" />
  </AccordionItem>

  <AccordionItem title="Remove a member">
<BlueprintViewer title="Remove a member" src="/blueprints/crossplay-kick-member.txt" />
  </AccordionItem>

  <AccordionItem title="Leave a lobby">
<BlueprintViewer title="Leave a lobby" src="/blueprints/crossplay-leave-lobby.txt" />
  </AccordionItem>
</Accordion>

**C++**

```cpp
#include "Engine/GameInstance.h"
#include "Lobbies/UCIKLobbySubsystem.h"
#include "UCIKCore.h"

namespace LobbyExamples
{
UUCIKLobbySubsystem *Service(UGameInstance &Game)
{
    auto *Lobbies = Game.GetSubsystem<UUCIKLobbySubsystem>();
    if (!Lobbies)
        UE_LOG(LogUCIK, Warning, TEXT("Lobby subsystem is unavailable"));
    return Lobbies;
}

template <typename T> FBetideCallback<T> Report(UGameInstance &Game, FString Operation)
{
    return FBetideCallback<T>::CreateWeakLambda(
        &Game, [Operation = MoveTemp(Operation)](const TBetideResult<T> &Result) {
            if (!Result.bOk)
            {
                UE_LOG(LogUCIK, Warning, TEXT("%s: %s"), *Operation, *Result.Error.GetLogString());
                return;
            }
            UE_LOG(LogUCIK, Log, TEXT("%s completed"), *Operation);
        });
}

void ExpandParty(UGameInstance &Game)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    FUCIK_LobbyUpdateSettings Update;
    Update.MaxMembers = 6;
    Update.bUpdateMaxMembers = true;
    Lobbies->UpdateLobby(TEXT("GameLobby"), Update,
                         Report<FUCIK_Lobby>(Game, TEXT("Expand party")));
}

void RefreshPartyMembers(UGameInstance &Game)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    Lobbies->QueryLobbyMembers(
        TEXT("GameLobby"), FBetideCallback<TArray<FUCIK_Member>>::CreateWeakLambda(
                   &Game, [](const TBetideResult<TArray<FUCIK_Member>> &Result) {
                       if (!Result.bOk)
                       {
                           UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                           return;
                       }
                       UE_LOG(LogUCIK, Log, TEXT("Members: %d"), Result.GetValue().Num());
                   }));
}

void SetReady(UGameInstance &Game, bool bReady)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    FUCIK_Attribute Value;
    Value.Type = EUCIK_AttributeType::Bool;
    Value.BoolValue = bReady;
    TMap<FString, FUCIK_Attribute> Attributes;
    Attributes.Add(TEXT("Ready"), Value);
    Lobbies->UpdateLobbyMemberAttributes(TEXT("GameLobby"), Attributes, {}, Report<bool>(Game, TEXT("Set ready")));
}

void SendPartyChat(UGameInstance &Game, const FString &Message)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    Lobbies->SendLobbyMessage(TEXT("GameLobby"), Message, Report<bool>(Game, TEXT("Send chat")));
}

void TransferPartyOwnership(UGameInstance &Game, const FUCIK_PlayerHandle &Member)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    Lobbies->PromoteLobbyMember(TEXT("GameLobby"), Member, Report<bool>(Game, TEXT("Transfer ownership")));
}

void RemovePartyMember(UGameInstance &Game, const FUCIK_PlayerHandle &Member)
{
    auto *Lobbies = Service(Game);
    if (!Lobbies)
        return;
    Lobbies->KickLobbyMember(TEXT("GameLobby"), Member, Report<bool>(Game, TEXT("Request member removal")));
}

void LeaveParty(UGameInstance &Game)
{
    if (auto *Lobbies = Service(Game))
        Lobbies->LeaveLobby(TEXT("GameLobby"), Report<bool>(Game, TEXT("Leave party")));
}
} // namespace LobbyExamples
```

Bind member, owner, message, and lobby-update events before joining. Search results may omit member details; query members after joining. Your game decides when everyone is ready.

<Accordion>
  <AccordionItem title="Updates, ownership, and cleanup">

`FUCIK_LobbyUpdateSettings` separates changed and removed attributes. Set `bUpdateJoinPolicy` when changing access; a zero `MaxMembers` leaves capacity unchanged. Keep `UCIK_` metadata reserved for the plugin.

Owner actions require ownership. Steam removes a member through a marker that the member's client observes and obeys; use server-side kicks for authoritative match removal.

**Destroy Crossplay Lobby** leaves the local membership. Do not assume it disconnects every other member. See [recovery](/crossplay/recovery) for clearing saved rejoin state after an intentional exit.

  </AccordionItem>
</Accordion>
