---
title: "Create and join sessions"
description: "Discover a game session and join it with a consistent local name."
seo:
  noindex: true
---

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

[Sign in](/crossplay/identity) and configure **Sessions** and **Transport** in **Betide → Crossplay**. Sessions advertise a match; they do not launch a server process.

## Create a session

**Blueprint**

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

**C++**

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

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

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

    FUCIK_SessionSettings Settings;
    Settings.MaxPlayers = 4;
    Settings.bAdvertise = true;
    Service->CreateSession(TEXT("GameSession"), Settings,
                           FBetideCallback<FUCIK_Session>::CreateWeakLambda(
                               &GameInstance, [](const TBetideResult<FUCIK_Session> &Result) {
                                   if (!Result.bOk)
                                   {
                                       UE_LOG(LogUCIK, Warning, TEXT("%s"),
                                              *Result.Error.GetLogString());
                                       return;
                                   }
                                   UE_LOG(LogUCIK, Log, TEXT("Session created"));
                               }));
}
```

Start the [listen server](/crossplay/listen-server) or [dedicated server](/crossplay/dedicated-server) before expecting a usable destination.

## Find sessions

**Blueprint**

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

**C++**

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

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

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

    FUCIK_SessionSearch Search;
    Search.MaxResults = 20;
    Search.MinOpenSlots = 1;
    Service->FindSessions(
        Search, FBetideCallback<TArray<FUCIK_Session>>::CreateWeakLambda(
                    &GameInstance, [](const TBetideResult<TArray<FUCIK_Session>> &Result) {
                        if (!Result.bOk)
                        {
                            UE_LOG(LogUCIK, Warning, TEXT("%s"), *Result.Error.GetLogString());
                            return;
                        }
                        UE_LOG(LogUCIK, Log, TEXT("Matches: %d"), Result.GetValue().Num());
                    }));
}
```
## Join a session

**Blueprint**

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

**C++**

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

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

void JoinMatch(UGameInstance &GameInstance, const FUCIK_Session &SelectedSession)
{
    auto *Service = GameInstance.GetSubsystem<UUCIKSessionSubsystem>();
    if (!Service)
        return;

    Service->JoinSession(SelectedSession, true,
                         FBetideCallback<FUCIK_Session>::CreateWeakLambda(
                             &GameInstance, [](const TBetideResult<FUCIK_Session> &Result) {
                                 if (!Result.bOk)
                                 {
                                     UE_LOG(LogUCIK, Warning, TEXT("%s"),
                                            *Result.Error.GetLogString());
                                     return;
                                 }
                                 UE_LOG(LogUCIK, Log, TEXT("Session joined"));
                             }));
}
```

Keep the join flow in your **Game Instance** Blueprint so its callbacks survive travel. Keep the complete search result. An unnamed result is joined under **GameSession**; an explicit name is preserved. Turn off **Auto Travel** if your game controls travel separately.

## Match lifecycle

**Blueprint**

<Accordion>
  <AccordionItem title="Start a match">
<BlueprintViewer title="Start a match" src="/blueprints/crossplay-start-session.txt" />
  </AccordionItem>
  <AccordionItem title="End a match">
<BlueprintViewer title="End a match" src="/blueprints/crossplay-end-session.txt" />
  </AccordionItem>
  <AccordionItem title="Close a match to new players">
<BlueprintViewer title="Close a match to new players" src="/blueprints/crossplay-update-session.txt" />
  </AccordionItem>
  <AccordionItem title="Register a player">
<BlueprintViewer title="Register a player" src="/blueprints/crossplay-register-player.txt" />
  </AccordionItem>
  <AccordionItem title="Unregister a player">
<BlueprintViewer title="Unregister a player" src="/blueprints/crossplay-unregister-player.txt" />
  </AccordionItem>
  <AccordionItem title="Leave a session">
<BlueprintViewer title="Leave a session" src="/blueprints/crossplay-leave-session.txt" />
  </AccordionItem>
</Accordion>

**C++**

```cpp
#include "Engine/GameInstance.h"
#include "Sessions/UCIKSessionSubsystem.h"
#include "UCIKCore.h"

namespace SessionExamples
{
UUCIKSessionSubsystem *Service(UGameInstance &Game)
{
    auto *Sessions = Game.GetSubsystem<UUCIKSessionSubsystem>();
    if (!Sessions)
        UE_LOG(LogUCIK, Warning, TEXT("Session subsystem is unavailable"));
    return Sessions;
}

FBetideCallback<bool> Report(UGameInstance &Game, FString Operation)
{
    return FBetideCallback<bool>::CreateWeakLambda(
        &Game, [Operation = MoveTemp(Operation)](const TBetideResult<bool> &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 StartMatch(UGameInstance &Game)
{
    if (auto *Sessions = Service(Game))
        Sessions->StartSession(TEXT("GameSession"), Report(Game, TEXT("Start match")));
}

void EndMatch(UGameInstance &Game)
{
    if (auto *Sessions = Service(Game))
        Sessions->EndSession(TEXT("GameSession"), Report(Game, TEXT("End match")));
}

void CloseMatch(UGameInstance &Game)
{
    auto *Sessions = Service(Game);
    if (!Sessions)
        return;
    FUCIK_SessionUpdateSettings Update;
    Update.JoinPolicy = EUCIK_JoinPolicy::InviteOnly;
    Update.bUpdateJoinPolicy = true;
    Sessions->UpdateSession(TEXT("GameSession"), Update,
                            FBetideCallback<FUCIK_Session>::CreateWeakLambda(
                                &Game, [](const TBetideResult<FUCIK_Session> &Result) {
                                    if (!Result.bOk)
                                        UE_LOG(LogUCIK, Warning, TEXT("Close match: %s"),
                                               *Result.Error.GetLogString());
                                }));
}

void PlayerJoinedMatch(UGameInstance &Game, const FUCIK_PlayerHandle &Player)
{
    if (auto *Sessions = Service(Game))
        Sessions->RegisterPlayers(TEXT("GameSession"), {Player}, false,
                                  Report(Game, TEXT("Register player")));
}

void PlayerLeftMatch(UGameInstance &Game, const FUCIK_PlayerHandle &Player)
{
    if (auto *Sessions = Service(Game))
        Sessions->UnregisterPlayers(TEXT("GameSession"), {Player},
                                    Report(Game, TEXT("Unregister player")));
}

void LeaveMatch(UGameInstance &Game)
{
    if (auto *Sessions = Service(Game))
        Sessions->LeaveSession(TEXT("GameSession"), Report(Game, TEXT("Leave session")));
}
} // namespace SessionExamples
```

<Accordion>
  <AccordionItem title="Provider differences">

EOS sessions and lobbies are separate services. Steam's OSSv2 listen sessions use Steam lobbies; dedicated sessions use server-browser entries. Crossplay searches combine these sources.

Session invites target the primary provider; mirrors are lobbies. Use [lobby invites](/crossplay/parties-invites) for cross-store parties. **Show Crossplay Session Invite UI** opens the Steam overlay; EOS has no invite overlay. **Join Crossplay Session By Handle** joins a session from friend presence or a mirror lobby. **Kick Crossplay Session Member** is EOS only, and **Promote Crossplay Session Member** needs a provider that can hand a session over (EOS). Registration is not supported by every provider.

Start/End change service state; they do not open or close a map. **Destroy Crossplay Session** calls the subsystem's `LeaveSession`. Keep `UCIK_` attributes reserved for the plugin.

  </AccordionItem>
</Accordion>
