Sessions
Create and manage game sessions for dedicated server matchmaking
Sessions are used for dedicated server matchmaking where a server hosts the game and players connect to it. Unlike lobbies, sessions don't provide real-time updates or built-in voice chat.
Sessions vs Lobbies: Use sessions for dedicated servers. Use lobbies for P2P games where players gather before matches. Lobbies are easier to manage with automatic updates and voice chat.
Create a Session

Parameters:
| Parameter | Description |
|---|---|
| Session Name | Local identifier for this session (default: "GameSession") |
| Settings | Session configuration (connections, advertising, dedicated server, etc.) |
#include "EIKCore/Public/EIKOnlineServicesFactory.h"
#include "EIKCore/Public/Interfaces/IEIKSessions.h"
void UMyClass::CreateSession()
{
TSharedPtr<IEIKOnlineServices> Services = FEIKOnlineServicesFactory::Get(GetWorld());
if (!Services.IsValid()) return;
TSharedPtr<IEIKSessions> Sessions = Services->GetSessionsInterface();
if (!Sessions.IsValid()) return;
FEIKSessionSettings Settings;
Settings.NumPublicConnections = 16;
Settings.bShouldAdvertise = true;
Settings.bIsDedicatedServer = true;
Settings.bAllowJoinInProgress = true;
Settings.bUsePresence = false;
Settings.Attributes.Add(TEXT("MapName"), FEIKSessionAttribute::String(TEXT("Forest")));
Settings.Attributes.Add(TEXT("GameMode"), FEIKSessionAttribute::String(TEXT("Deathmatch")));
Sessions->CreateSession(0, FName("GameSession"), Settings,
FEIKAsyncCallback<FEIKCreateSessionResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKCreateSessionResult>& Result)
{
if (Result.IsSuccessful())
{
FString SessionId = Result.GetValue().Session.SessionId.ToString();
UE_LOG(LogTemp, Log, TEXT("Session created: %s"), *SessionId);
}
}));
}#include "Online/OnlineServices.h"
#include "Online/Sessions.h"
using namespace UE::Online;
void UMyClass::CreateSession()
{
IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
if (!Services.IsValid()) return;
ISessionsPtr Sessions = Services->GetSessionsInterface();
if (!Sessions.IsValid()) return;
FCreateSession::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.SessionName = FName("GameSession");
Params.bPresenceEnabled = false;
Params.SessionSettings.NumMaxConnections = 16;
Params.SessionSettings.bAllowNewMembers = true;
Params.SessionSettings.JoinPolicy = ESessionJoinPolicy::Public;
Params.SessionSettings.SchemaName = FName("DefaultSessionSchema");
Params.SessionSettings.CustomSettings.Add(
FSchemaAttributeId(TEXT("MapName")),
FCustomSessionSetting{FSchemaVariant(TEXT("Forest")), ESchemaAttributeVisibility::Public});
Sessions->CreateSession(MoveTemp(Params))
.OnComplete([this](const TOnlineResult<FCreateSession>& Result)
{
if (Result.IsOk())
{
UE_LOG(LogTemp, Log, TEXT("Session created!"));
}
});
}#include "OnlineSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"
void UMyClass::CreateSession()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
if (!OnlineSub) return;
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
if (!Sessions.IsValid()) return;
FOnlineSessionSettings Settings;
Settings.NumPublicConnections = 16;
Settings.bShouldAdvertise = true;
Settings.bIsDedicated = true;
Settings.bAllowJoinInProgress = true;
Settings.bUsesPresence = false;
Settings.bAllowInvites = true;
Settings.Set(FName(TEXT("MapName")), FString(TEXT("Forest")), EOnlineDataAdvertisementType::ViaOnlineService);
Settings.Set(FName(TEXT("GameMode")), FString(TEXT("Deathmatch")), EOnlineDataAdvertisementType::ViaOnlineService);
Sessions->AddOnCreateSessionCompleteDelegate_Handle(
FOnCreateSessionCompleteDelegate::CreateLambda(
[](FName SessionName, bool bWasSuccessful)
{
if (bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Session created: %s"), *SessionName.ToString());
}
}));
Sessions->CreateSession(0, FName("GameSession"), Settings);
}Session Settings
| Setting | Type | Default | Description |
|---|---|---|---|
| NumPublicConnections | int | 16 | Maximum public player slots |
| NumPrivateConnections | int | 0 | Reserved slots for invited players |
| bShouldAdvertise | bool | true | Make session discoverable |
| bIsDedicatedServer | bool | false | Running on dedicated server |
| bAllowJoinInProgress | bool | true | Allow joining after start |
| bIsLANMatch | bool | false | LAN-only session |
| bUsePresence | bool | false | Show in social overlay |
| bAllowInvites | bool | true | Allow sending invites |
| bAntiCheatProtected | bool | false | Require anti-cheat |
| Attributes | Map | empty | Custom searchable key-value data |
Find Sessions

Use Attributes to filter results. Only sessions matching all specified attributes are returned.
void UMyClass::FindSessions()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
FEIKSessionSearchParams SearchParams;
SearchParams.MaxResults = 20;
SearchParams.Attributes.Add(TEXT("GameMode"), FEIKSessionAttribute::String(TEXT("Deathmatch")));
Sessions->FindSessions(0, SearchParams,
FEIKAsyncCallback<FEIKFindSessionsResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKFindSessionsResult>& Result)
{
if (Result.IsSuccessful())
{
for (const FEIKSessionSearchResult& Session : Result.GetValue().Results)
{
UE_LOG(LogTemp, Log, TEXT("Found: %s (%d/%d) Ping: %dms"),
*Session.Session.SessionId.ToString(),
Session.Session.NumCurrentPlayers,
Session.Session.Settings.NumPublicConnections,
Session.PingInMs);
}
}
}));
}void UMyClass::FindSessions()
{
IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
ISessionsPtr Sessions = Services->GetSessionsInterface();
FFindSessions::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.MaxResults = 20;
Params.Filters.Add(FFindSessionsSearchFilter{
FSchemaAttributeId(TEXT("GameMode")),
ESchemaAttributeComparisonOp::Equals,
FSchemaVariant(TEXT("Deathmatch"))
});
Sessions->FindSessions(MoveTemp(Params))
.OnComplete([this](const TOnlineResult<FFindSessions>& Result)
{
if (Result.IsOk())
{
for (const FOnlineSessionId& SessionId : Result.GetOkValue().FoundSessionIds)
{
UE_LOG(LogTemp, Log, TEXT("Found session: %s"), *SessionId.ToString());
}
}
});
}void UMyClass::FindSessions()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
SearchSettings = MakeShareable(new FOnlineSessionSearch());
SearchSettings->MaxSearchResults = 20;
SearchSettings->QuerySettings.Set(FName(TEXT("GameMode")), FString(TEXT("Deathmatch")), EOnlineComparisonOp::Equals);
Sessions->AddOnFindSessionsCompleteDelegate_Handle(
FOnFindSessionsCompleteDelegate::CreateLambda(
[this](bool bWasSuccessful)
{
if (bWasSuccessful)
{
for (const FOnlineSessionSearchResult& Result : SearchSettings->SearchResults)
{
UE_LOG(LogTemp, Log, TEXT("Found session: %d/%d slots"),
Result.Session.NumOpenPublicConnections,
Result.Session.SessionSettings.NumPublicConnections);
}
}
}));
Sessions->FindSessions(0, SearchSettings.ToSharedRef());
}Join a Session

| Parameter | Description |
|---|---|
| Session To Join | The session from search results |
| Local Session Name | Local identifier for tracking |
| Auto Travel | Automatically travel to server after joining |
void UMyClass::JoinSession(const FEIKSessionSearchResult& SearchResult)
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
Sessions->JoinSession(0, FName("GameSession"), SearchResult,
FEIKAsyncCallback<FEIKJoinSessionResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKJoinSessionResult>& Result)
{
if (Result.IsSuccessful())
{
const FEIKJoinSessionResult& JoinResult = Result.GetValue();
if (JoinResult.ResultCode == EEIKJoinSessionResult::Success)
{
// Travel to server
APlayerController* PC = GetWorld()->GetFirstPlayerController();
if (PC)
{
PC->ClientTravel(JoinResult.ConnectString, ETravelType::TRAVEL_Absolute);
}
}
}
}));
}void UMyClass::JoinSession(const FOnlineSessionId& SessionId)
{
ISessionsPtr Sessions = GetSessionsInterface();
FJoinSession::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.SessionName = FName("GameSession");
Params.SessionId = SessionId;
Params.bPresenceEnabled = false;
Sessions->JoinSession(MoveTemp(Params))
.OnComplete([this](const TOnlineResult<FJoinSession>& Result)
{
if (Result.IsOk())
{
UE_LOG(LogTemp, Log, TEXT("Joined session!"));
}
});
}void UMyClass::JoinSession(const FOnlineSessionSearchResult& SearchResult)
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
Sessions->AddOnJoinSessionCompleteDelegate_Handle(
FOnJoinSessionCompleteDelegate::CreateLambda(
[this, Sessions](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
if (Result == EOnJoinSessionCompleteResult::Success)
{
FString ConnectString;
if (Sessions->GetResolvedConnectString(SessionName, ConnectString))
{
APlayerController* PC = GetWorld()->GetFirstPlayerController();
if (PC)
{
PC->ClientTravel(ConnectString, ETravelType::TRAVEL_Absolute);
}
}
}
}));
Sessions->JoinSession(0, FName("GameSession"), SearchResult);
}Session Lifecycle
Sessions have a lifecycle that must be managed:
| State | Description |
|---|---|
| NoSession | No session exists |
| Creating | Session being created |
| Pending | Session created, waiting to start |
| Starting | Session starting |
| InProgress | Game is running |
| Ending | Session ending |
| Ended | Game finished, session still exists |
| Destroying | Session being destroyed |
Start Session
Mark a session as "in progress" when the game begins:

void UMyClass::StartSession()
{
// EIKCore
Sessions->StartSession(FName("GameSession"),
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
UE_LOG(LogTemp, Log, TEXT("Session started"));
}));
// Or OSS v1
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
Sessions->StartSession(FName("GameSession"));
}End Session
Mark a session as "ended" when the game finishes (session still exists for stats/cleanup):

void UMyClass::EndSession()
{
Sessions->EndSession(FName("GameSession"),
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
UE_LOG(LogTemp, Log, TEXT("Session ended"));
}));
}Destroy Session
Remove the session completely:

void UMyClass::DestroySession()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
Sessions->DestroySession(FName("GameSession"),
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
UE_LOG(LogTemp, Log, TEXT("Session destroyed"));
}));
}void UMyClass::DestroySession()
{
ISessionsPtr Sessions = GetSessionsInterface();
FLeaveSession::Params Params;
Params.LocalAccountId = GetLocalAccountId();
Params.SessionName = FName("GameSession");
Params.bDestroySession = true;
Sessions->LeaveSession(MoveTemp(Params))
.OnComplete([](const TOnlineResult<FLeaveSession>& Result) {});
}void UMyClass::DestroySession()
{
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
Sessions->AddOnDestroySessionCompleteDelegate_Handle(
FOnDestroySessionCompleteDelegate::CreateLambda(
[](FName SessionName, bool bWasSuccessful)
{
UE_LOG(LogTemp, Log, TEXT("Session destroyed: %s"), *SessionName.ToString());
}));
Sessions->DestroySession(FName("GameSession"));
}Update Session
Modify session settings while it's running:

void UMyClass::UpdateSession()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
FEIKSessionSettings NewSettings;
NewSettings.bAllowJoinInProgress = false;
NewSettings.Attributes.Add(TEXT("MapName"), FEIKSessionAttribute::String(TEXT("Desert")));
Sessions->UpdateSession(0, FName("GameSession"), NewSettings,
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
UE_LOG(LogTemp, Log, TEXT("Session updated"));
}));
}Player Registration
Register players when they join, unregister when they leave. This tracks player count and enables invites.
Register - Call in GameMode when a player joins:

Unregister - Call when a player leaves:

// In GameMode - when player joins
void AMyGameMode::PostLogin(APlayerController* NewPlayer)
{
Super::PostLogin(NewPlayer);
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
FEIKAccountId PlayerId = GetPlayerAccountId(NewPlayer);
Sessions->RegisterPlayer(FName("GameSession"), PlayerId, false,
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result) {}));
}
// When player leaves
void AMyGameMode::Logout(AController* Exiting)
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
FEIKAccountId PlayerId = GetPlayerAccountId(Cast<APlayerController>(Exiting));
Sessions->UnregisterPlayer(FName("GameSession"), PlayerId,
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result) {}));
Super::Logout(Exiting);
}Session Events
Listen for session state changes.
Use EIK Session Events Subsystem to bind to events:
- Get the subsystem:
Get Game Instance→Get Subsystem→EIK Session Events Subsystem - Bind to events like
On Player Joined,On Session Updated,On Invite Received
| Event | When Fired |
|---|---|
| On Session Created | Session successfully created |
| On Session Joined | Local player joins a session |
| On Session Left | Local player leaves a session |
| On Session Updated | Session settings changed |
| On Player Joined | Another player joins |
| On Player Left | Another player leaves |
| On Owner Changed | Session ownership transferred |
| On Invite Received | Received a session invite |
void UMyClass::SetupSessionEvents()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
if (!Sessions.IsValid()) return;
Sessions->OnPlayerJoined().AddUObject(this, &UMyClass::HandlePlayerJoined);
Sessions->OnPlayerLeft().AddUObject(this, &UMyClass::HandlePlayerLeft);
Sessions->OnSessionUpdated().AddUObject(this, &UMyClass::HandleSessionUpdated);
Sessions->OnInviteReceived().AddUObject(this, &UMyClass::HandleInviteReceived);
}
void UMyClass::HandlePlayerJoined(const FEIKSessionPlayerJoinedEvent& Event)
{
UE_LOG(LogTemp, Log, TEXT("Player joined session: %s"), *Event.SessionName.ToString());
}
void UMyClass::HandlePlayerLeft(const FEIKSessionPlayerLeftEvent& Event)
{
UE_LOG(LogTemp, Log, TEXT("Player left, reason: %d"), (int32)Event.Reason);
}Invitations
Send an Invite

void UMyClass::InviteFriend(const FEIKAccountId& FriendId)
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
Sessions->SendInvite(0, FName("GameSession"), FriendId,
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
if (Result.IsSuccessful())
{
UE_LOG(LogTemp, Log, TEXT("Invite sent!"));
}
}));
}Handle Incoming Invites

- Get EIK Session Events Subsystem and bind to
On Session Invite Received - Break the event to get sender info and session details
- Use Accept EIK Session Invite to join, or reject the invite
void UMyClass::HandleInviteReceived(const FEIKSessionInviteReceivedEvent& Event)
{
PendingInviteId = Event.InviteId;
ShowInviteUI(Event.SenderId);
}
void UMyClass::AcceptInvite()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
// Join the session from the invite
Sessions->JoinSessionById(0, FName("InvitedSession"), PendingSessionId, false,
FEIKAsyncCallback<FEIKJoinSessionResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKJoinSessionResult>& Result)
{
if (Result.IsSuccessful())
{
// Travel to server
}
}));
}
void UMyClass::DeclineInvite()
{
TSharedPtr<IEIKSessions> Sessions = GetSessionsInterface();
Sessions->RejectInvite(0, PendingInviteId,
FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
{
UE_LOG(LogTemp, Log, TEXT("Invite declined"));
}));
}Dedicated Server Flow
Typical flow for a dedicated server:
// Server startup
void AMyGameMode::BeginPlay()
{
Super::BeginPlay();
if (IsRunningDedicatedServer())
{
FEIKSessionSettings Settings;
Settings.NumPublicConnections = 16;
Settings.bIsDedicatedServer = true;
Settings.bShouldAdvertise = true;
Settings.Attributes.Add(TEXT("ServerName"), FEIKSessionAttribute::String(TEXT("My Server")));
Sessions->CreateSession(0, FName("GameSession"), Settings,
FEIKAsyncCallback<FEIKCreateSessionResult>::CreateLambda(
[this](const TEIKAsyncResult<FEIKCreateSessionResult>& Result)
{
if (Result.IsSuccessful())
{
// Session ready, server can accept players
}
}));
}
}
// When match starts
void AMyGameMode::StartMatch()
{
Sessions->StartSession(FName("GameSession"), ...);
}
// When match ends
void AMyGameMode::EndMatch()
{
Sessions->EndSession(FName("GameSession"), ...);
// Optionally destroy and recreate for next match
}Helper Functions
The Session Library provides synchronous helper functions:
| Function | Description |
|---|---|
GetSessionInfo(SessionName) | Get session details |
GetSessionState(SessionName) | Get current state |
GetAllSessionNames() | List all local sessions |
IsPlayerInSession(SessionName, PlayerId) | Check if player is registered |
GetResolvedConnectString(SessionName) | Get server address for travel |
HasSessionAvailableSlots(SessionName) | Check if session has open slots |
GetSessionAvailableSlots(SessionName) | Get number of open slots |