EOS Integration KitAlpha
v5.0.1

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

Create EIK Session node
Copy and paste into Unreal Engine Blueprint editor

Parameters:

ParameterDescription
Session NameLocal identifier for this session (default: "GameSession")
SettingsSession 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

SettingTypeDefaultDescription
NumPublicConnectionsint16Maximum public player slots
NumPrivateConnectionsint0Reserved slots for invited players
bShouldAdvertisebooltrueMake session discoverable
bIsDedicatedServerboolfalseRunning on dedicated server
bAllowJoinInProgressbooltrueAllow joining after start
bIsLANMatchboolfalseLAN-only session
bUsePresenceboolfalseShow in social overlay
bAllowInvitesbooltrueAllow sending invites
bAntiCheatProtectedboolfalseRequire anti-cheat
AttributesMapemptyCustom searchable key-value data

Find Sessions

Find EIK Sessions node
Copy and paste into Unreal Engine Blueprint editor

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

Join EIK Session node
Copy and paste into Unreal Engine Blueprint editor
ParameterDescription
Session To JoinThe session from search results
Local Session NameLocal identifier for tracking
Auto TravelAutomatically 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:

StateDescription
NoSessionNo session exists
CreatingSession being created
PendingSession created, waiting to start
StartingSession starting
InProgressGame is running
EndingSession ending
EndedGame finished, session still exists
DestroyingSession being destroyed

Start Session

Mark a session as "in progress" when the game begins:

Start EIK Session node
Copy and paste into Unreal Engine Blueprint editor
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):

End EIK Session node
Copy and paste into Unreal Engine Blueprint editor
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:

Destroy EIK Session node
Copy and paste into Unreal Engine Blueprint editor
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:

Update EIK Session node
Copy and paste into Unreal Engine Blueprint editor
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:

Register Player In EIK Session node
Copy and paste into Unreal Engine Blueprint editor

Unregister - Call when a player leaves:

Unregister Player From EIK Session node
Copy and paste into Unreal Engine Blueprint editor
// 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:

  1. Get the subsystem: Get Game InstanceGet SubsystemEIK Session Events Subsystem
  2. Bind to events like On Player Joined, On Session Updated, On Invite Received
EventWhen Fired
On Session CreatedSession successfully created
On Session JoinedLocal player joins a session
On Session LeftLocal player leaves a session
On Session UpdatedSession settings changed
On Player JoinedAnother player joins
On Player LeftAnother player leaves
On Owner ChangedSession ownership transferred
On Invite ReceivedReceived 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

Send Session Invite node
Copy and paste into Unreal Engine Blueprint editor
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

Session Events Subsystem with invite handling flow
Copy and paste into Unreal Engine Blueprint editor
  1. Get EIK Session Events Subsystem and bind to On Session Invite Received
  2. Break the event to get sender info and session details
  3. 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:

FunctionDescription
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

On this page