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
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
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
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
Loading the interactive viewer.
End a match
Loading the interactive viewer.
Close a match to new players
Loading the interactive viewer.
Register a player
Loading the interactive viewer.
Unregister a player
Loading the interactive viewer.
Leave a session
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 SessionExamplesProvider 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.