Skip to content
UCIK Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

Create and join sessions

Discover a game session and join it with a consistent local name.

Sign in and configure Sessions and Transport in Betide → Crossplay. Sessions advertise a match; they do not launch a server process.

Create a session

Create a session
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: UCIKCore, BetideCore.

#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 or dedicated server before expecting a usable destination.

Find sessions

Find sessions
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: UCIKCore, BetideCore.

#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

Join a session
Preparing your graph…

Loading the interactive viewer.

Game module dependencies: UCIKCore, BetideCore.

#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

Start a match
Start a match
Preparing your graph…

Loading the interactive viewer.

End a match
End a match
Preparing your graph…

Loading the interactive viewer.

Close a match to new players
Close a match to new players
Preparing your graph…

Loading the interactive viewer.

Register a player
Register a player
Preparing your graph…

Loading the interactive viewer.

Unregister a player
Unregister a player
Preparing your graph…

Loading the interactive viewer.

Leave a session
Leave a session
Preparing your graph…

Loading the interactive viewer.

#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
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 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.

Was this page helpful?