EOS Integration KitAlpha
v5.0.1

Lobbies

Create and manage lobbies for players to gather before matches

Lobbies are persistent gathering spaces where players can form teams, chat, and configure game settings before starting a match. They provide real-time updates, voice chat integration, and host migration.

Lobbies vs Sessions: Use lobbies for P2P games where players gather before matches. Use sessions for dedicated servers.

Create a Lobby

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

Parameters:

ParameterDescription
Lobby NameLocal identifier for this lobby (default: "GameLobby")
SettingsLobby configuration (max members, join policy, voice chat, etc.)
#include "EIKCore/Public/EIKOnlineServicesFactory.h"
#include "EIKCore/Public/Interfaces/IEIKLobbies.h"

void UMyClass::CreateLobby()
{
    TSharedPtr<IEIKOnlineServices> Services = FEIKOnlineServicesFactory::Get(GetWorld());
    if (!Services.IsValid()) return;

    TSharedPtr<IEIKLobbies> Lobbies = Services->GetLobbiesInterface();
    if (!Lobbies.IsValid()) return;

    FEIKLobbySettings Settings;
    Settings.MaxMembers = 4;
    Settings.JoinPolicy = EEIKJoinPolicy::PublicAdvertised;
    Settings.bEnableVoiceChat = true;
    Settings.bEnableHostMigration = true;
    Settings.Attributes.Add(TEXT("MapName"), FEIKLobbyAttribute::String(TEXT("Forest")));

    TMap<FString, FEIKLobbyAttribute> MemberAttrs;
    MemberAttrs.Add(TEXT("Team"), FEIKLobbyAttribute::String(TEXT("Blue")));

    Lobbies->CreateLobby(0, FName("GameLobby"), Settings, MemberAttrs,
        FEIKAsyncCallback<FEIKCreateLobbyResult>::CreateLambda(
            [this](const TEIKAsyncResult<FEIKCreateLobbyResult>& Result)
            {
                if (Result.IsSuccessful())
                {
                    FString LobbyId = Result.GetValue().Lobby.LobbyId.ToString();
                    UE_LOG(LogTemp, Log, TEXT("Lobby created: %s"), *LobbyId);
                }
            }));
}
#include "Online/OnlineServices.h"
#include "Online/Lobbies.h"

using namespace UE::Online;

void UMyClass::CreateLobby()
{
    IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
    if (!Services.IsValid()) return;

    ILobbiesPtr Lobbies = Services->GetLobbiesInterface();
    if (!Lobbies.IsValid()) return;

    FCreateLobby::Params Params;
    Params.LocalAccountId = Services->GetAuthInterface()->GetLocalOnlineUserByPlatformUserId(
        FPlatformMisc::GetPlatformUserForUserIndex(0))->GetAccountId();
    Params.LocalName = FName("GameLobby");
    Params.SchemaId = FName("DefaultLobbySchema");
    Params.MaxMembers = 4;
    Params.JoinPolicy = ELobbyJoinPolicy::PublicAdvertised;
    Params.Attributes.Add(FSchemaAttributeId(TEXT("MapName")), FSchemaVariant(TEXT("Forest")));

    Lobbies->CreateLobby(MoveTemp(Params))
        .OnComplete([this](const TOnlineResult<FCreateLobby>& Result)
        {
            if (Result.IsOk())
            {
                UE_LOG(LogTemp, Log, TEXT("Lobby created!"));
            }
        });
}
#include "OnlineSubsystem.h"
#include "Interfaces/OnlineSessionInterface.h"

void UMyClass::CreateLobby()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    if (!OnlineSub) return;

    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
    if (!Sessions.IsValid()) return;

    FOnlineSessionSettings Settings;
    Settings.bUseLobbiesIfAvailable = true;  // Use lobby instead of session
    Settings.NumPublicConnections = 4;
    Settings.bShouldAdvertise = true;
    Settings.bUsesPresence = true;
    Settings.bAllowInvites = true;
    Settings.bAllowJoinInProgress = true;
    Settings.bUseLobbiesVoiceChatIfAvailable = true;
    Settings.Set(FName(TEXT("MapName")), FString(TEXT("Forest")), EOnlineDataAdvertisementType::ViaOnlineService);

    Sessions->AddOnCreateSessionCompleteDelegate_Handle(
        FOnCreateSessionCompleteDelegate::CreateLambda(
            [](FName SessionName, bool bWasSuccessful)
            {
                if (bWasSuccessful)
                {
                    UE_LOG(LogTemp, Log, TEXT("Lobby created: %s"), *SessionName.ToString());
                }
            }));

    Sessions->CreateSession(0, FName("GameLobby"), Settings);
}

Lobby Settings

SettingTypeDefaultDescription
MaxMembersint10Maximum players (2-64)
JoinPolicyenumPublicAdvertisedWho can find/join
bEnableVoiceChatboolfalseEnable built-in voice chat
bEnableHostMigrationbooltrueAuto-promote new host if owner leaves
bUsePresenceboolfalseShow in social overlay
bAllowInvitesbooltrueAllow sending invites
bEnableJoinByIdbooltrueAllow direct join via lobby ID
AttributesMapemptyCustom searchable key-value data

Join Policies

PolicySearchableJoin by IDInvite Only
PublicAdvertisedYesYesNo
PublicNotAdvertisedNoYesNo
InviteOnlyNoNoYes

Find Lobbies

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

Use Attributes to filter results. Only lobbies matching all specified attributes are returned.

void UMyClass::FindLobbies()
{
    TSharedPtr<IEIKOnlineServices> Services = FEIKOnlineServicesFactory::Get(GetWorld());
    TSharedPtr<IEIKLobbies> Lobbies = Services->GetLobbiesInterface();

    FEIKLobbySearchParams SearchParams;
    SearchParams.MaxResults = 20;
    SearchParams.Attributes.Add(TEXT("MapName"), FEIKLobbyAttribute::String(TEXT("Forest")));

    Lobbies->FindLobbies(0, SearchParams,
        FEIKAsyncCallback<FEIKFindLobbiesResult>::CreateLambda(
            [this](const TEIKAsyncResult<FEIKFindLobbiesResult>& Result)
            {
                if (Result.IsSuccessful())
                {
                    for (const FEIKLobby& Lobby : Result.GetValue().Results)
                    {
                        UE_LOG(LogTemp, Log, TEXT("Found: %s (%d/%d)"),
                            *Lobby.LobbyId.ToString(),
                            Lobby.GetMemberCount(),
                            Lobby.Settings.MaxMembers);
                    }
                }
            }));
}
void UMyClass::FindLobbies()
{
    IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
    ILobbiesPtr Lobbies = Services->GetLobbiesInterface();

    FFindLobbies::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.MaxResults = 20;
    Params.Filters.Add(FFindLobbySearchFilter{
        FSchemaAttributeId(TEXT("MapName")),
        ESchemaAttributeComparisonOp::Equals,
        FSchemaVariant(TEXT("Forest"))
    });

    Lobbies->FindLobbies(MoveTemp(Params))
        .OnComplete([this](const TOnlineResult<FFindLobbies>& Result)
        {
            if (Result.IsOk())
            {
                for (const TSharedRef<const FLobby>& Lobby : Result.GetOkValue().Lobbies)
                {
                    UE_LOG(LogTemp, Log, TEXT("Found lobby with %d members"),
                        Lobby->Members.Num());
                }
            }
        });
}
void UMyClass::FindLobbies()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    SearchSettings = MakeShareable(new FOnlineSessionSearch());
    SearchSettings->MaxSearchResults = 20;
    SearchSettings->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
    SearchSettings->QuerySettings.Set(FName(TEXT("MapName")), FString(TEXT("Forest")), EOnlineComparisonOp::Equals);

    Sessions->AddOnFindSessionsCompleteDelegate_Handle(
        FOnFindSessionsCompleteDelegate::CreateLambda(
            [this](bool bWasSuccessful)
            {
                if (bWasSuccessful)
                {
                    for (const FOnlineSessionSearchResult& Result : SearchSettings->SearchResults)
                    {
                        UE_LOG(LogTemp, Log, TEXT("Found lobby: %d/%d slots"),
                            Result.Session.NumOpenPublicConnections,
                            Result.Session.SessionSettings.NumPublicConnections);
                    }
                }
            }));

    Sessions->FindSessions(0, SearchSettings.ToSharedRef());
}

Join a Lobby

Join EIK Lobby node
Copy and paste into Unreal Engine Blueprint editor
ParameterDescription
Lobby To JoinThe lobby from search results
Local Lobby NameLocal identifier for tracking
Auto TravelAutomatically travel to lobby host after joining
void UMyClass::JoinLobby(const FEIKLobby& LobbyToJoin)
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();

    TMap<FString, FEIKLobbyAttribute> MemberAttrs;
    MemberAttrs.Add(TEXT("Ready"), FEIKLobbyAttribute::Bool(false));

    Lobbies->JoinLobby(0, FName("GameLobby"), LobbyToJoin, MemberAttrs,
        FEIKAsyncCallback<FEIKJoinLobbyResult>::CreateLambda(
            [this](const TEIKAsyncResult<FEIKJoinLobbyResult>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Log, TEXT("Joined lobby!"));
                }
            }));
}
void UMyClass::JoinLobby(const TSharedRef<const FLobby>& LobbyToJoin)
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    FJoinLobby::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LocalName = FName("GameLobby");
    Params.LobbyId = LobbyToJoin->LobbyId;

    Lobbies->JoinLobby(MoveTemp(Params))
        .OnComplete([this](const TOnlineResult<FJoinLobby>& Result)
        {
            if (Result.IsOk())
            {
                UE_LOG(LogTemp, Log, TEXT("Joined lobby!"));
            }
        });
}
void UMyClass::JoinLobby(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("GameLobby"), SearchResult);
}

Leave a Lobby

Leave EIK Lobby node
Copy and paste into Unreal Engine Blueprint editor
void UMyClass::LeaveLobby()
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();
    FEIKLobby CurrentLobby = Lobbies->GetLobbyByName(FName("GameLobby"));

    Lobbies->LeaveLobby(0, CurrentLobby.LobbyId,
        FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
        {
            UE_LOG(LogTemp, Log, TEXT("Left lobby"));
        }));
}
void UMyClass::LeaveLobby(const FLobbyId& LobbyId)
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    FLeaveLobby::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LobbyId = LobbyId;

    Lobbies->LeaveLobby(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FLeaveLobby>& Result) {});
}
void UMyClass::LeaveLobby()
{
    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("Left lobby: %s"), *SessionName.ToString());
            }));

    Sessions->DestroySession(FName("GameLobby"));
}

Lobby Events

Listen for real-time updates when lobby state changes.

Use EIK Lobby Events Subsystem to bind to events:

  1. Get the subsystem: Get Game InstanceGet SubsystemEIK Lobby Events Subsystem
  2. Bind to events like On Member Joined, On Lobby Left, On Invite Received
Lobby Events Subsystem binding
Copy and paste into Unreal Engine Blueprint editor
EventWhen Fired
On Lobby JoinedLocal player joins a lobby
On Lobby LeftLocal player leaves a lobby
On Member JoinedAnother player joins
On Member LeftAnother player leaves
On Owner ChangedHost migration occurred
On Lobby Attributes ChangedLobby settings changed
On Member Attributes ChangedA member's attributes changed
On Lobby Invite ReceivedReceived a lobby invite
void UMyClass::SetupLobbyEvents()
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();
    if (!Lobbies.IsValid()) return;

    Lobbies->OnMemberJoined().AddUObject(this, &UMyClass::HandleMemberJoined);
    Lobbies->OnMemberLeft().AddUObject(this, &UMyClass::HandleMemberLeft);
    Lobbies->OnOwnerChanged().AddUObject(this, &UMyClass::HandleOwnerChanged);
    Lobbies->OnInviteReceived().AddUObject(this, &UMyClass::HandleInviteReceived);
}

void UMyClass::HandleMemberJoined(const FEIKLobbyMemberJoinedEvent& Event)
{
    UE_LOG(LogTemp, Log, TEXT("Player joined: %s"), *Event.Member.DisplayName);
}

void UMyClass::HandleMemberLeft(const FEIKLobbyMemberLeftEvent& Event)
{
    UE_LOG(LogTemp, Log, TEXT("Player left, reason: %d"), (int32)Event.Reason);
}

void UMyClass::HandleOwnerChanged(const FEIKLobbyOwnerChangedEvent& Event)
{
    UE_LOG(LogTemp, Log, TEXT("New owner: %s"), *Event.NewOwnerId.ToString());
}

void UMyClass::HandleInviteReceived(const FEIKLobbyInviteReceivedEvent& Event)
{
    UE_LOG(LogTemp, Log, TEXT("Invite from: %s"), *Event.SenderId.ToString());
}
void UMyClass::SetupLobbyEvents()
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    Lobbies->OnLobbyMemberJoined().Add(this, &UMyClass::HandleMemberJoined);
    Lobbies->OnLobbyMemberLeft().Add(this, &UMyClass::HandleMemberLeft);
    Lobbies->OnLobbyLeaderChanged().Add(this, &UMyClass::HandleLeaderChanged);
    Lobbies->OnLobbyInvitationAdded().Add(this, &UMyClass::HandleInvitationAdded);
}

void UMyClass::HandleMemberJoined(const FLobbyMemberJoined& Event)
{
    UE_LOG(LogTemp, Log, TEXT("Member joined lobby"));
}
void UMyClass::SetupLobbyEvents()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    Sessions->AddOnSessionUserInviteAcceptedDelegate_Handle(
        FOnSessionUserInviteAcceptedDelegate::CreateUObject(
            this, &UMyClass::HandleInviteAccepted));

    Sessions->AddOnSessionParticipantsChangeDelegate_Handle(
        FOnSessionParticipantsChangeDelegate::CreateUObject(
            this, &UMyClass::HandleParticipantsChanged));
}

void UMyClass::HandleInviteAccepted(bool bWasSuccessful, int32 ControllerId,
    FUniqueNetIdPtr UserId, const FOnlineSessionSearchResult& InviteResult)
{
    if (bWasSuccessful)
    {
        UE_LOG(LogTemp, Log, TEXT("Invite accepted, joining lobby..."));
        JoinLobby(InviteResult);
    }
}

void UMyClass::HandleParticipantsChanged(FName SessionName, const FUniqueNetId& UniqueId, bool bJoined)
{
    UE_LOG(LogTemp, Log, TEXT("Participant %s: %s"),
        bJoined ? TEXT("joined") : TEXT("left"), *UniqueId.ToString());
}

Invitations

Send an Invite

Send Lobby Invite node
Copy and paste into Unreal Engine Blueprint editor
void UMyClass::InviteFriend(const FEIKAccountId& FriendId)
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();
    FEIKLobby Lobby = Lobbies->GetLobbyByName(FName("GameLobby"));

    Lobbies->SendInvite(0, Lobby.LobbyId, FriendId,
        FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
        {
            if (Result.IsSuccessful())
            {
                UE_LOG(LogTemp, Log, TEXT("Invite sent!"));
            }
        }));
}
void UMyClass::InviteFriend(const FAccountId& FriendId, const FLobbyId& LobbyId)
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    FInviteLobbyMember::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LobbyId = LobbyId;
    Params.TargetAccountId = FriendId;

    Lobbies->InviteLobbyMember(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FInviteLobbyMember>& Result) {});
}
void UMyClass::InviteFriend(const FUniqueNetId& FriendId)
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    Sessions->SendSessionInviteToFriend(0, FName("GameLobby"), FriendId);
}

Handle Incoming Invites

  1. Bind to On Lobby Invite Received event on the EIK Lobby Events Subsystem
  2. Show UI with invite details from the event (sender, lobby info)
  3. Use Accept EIK Lobby Invite or Reject EIK Lobby Invite nodes
Accept/Reject Lobby Invite nodes
Copy and paste into Unreal Engine Blueprint editor
NodeParameters
Accept EIK Lobby InviteLobby ID, Local Name, Auto Travel
Reject EIK Lobby InviteLobby ID
void UMyClass::HandleInviteReceived(const FEIKLobbyInviteReceivedEvent& Event)
{
    // Store invite for UI
    PendingInvite = Event;
    ShowInviteUI(Event.SenderId, Event.Lobby);
}

void UMyClass::AcceptInvite()
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();

    TMap<FString, FEIKLobbyAttribute> MemberAttrs;
    Lobbies->JoinLobbyById(0, FName("InvitedLobby"), PendingInvite.Lobby.LobbyId, MemberAttrs,
        FEIKAsyncCallback<FEIKJoinLobbyResult>::CreateLambda(
            [this](const TEIKAsyncResult<FEIKJoinLobbyResult>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Log, TEXT("Accepted invite, joined lobby!"));
                }
            }));
}

void UMyClass::DeclineInvite()
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();

    Lobbies->DeclineInvite(0, PendingInvite.Lobby.LobbyId,
        FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result)
        {
            UE_LOG(LogTemp, Log, TEXT("Declined invite"));
        }));
}
void UMyClass::HandleInvitationAdded(const FLobbyInvitationAdded& Event)
{
    PendingInvitation = Event.Invitation;
    ShowInviteUI();
}

void UMyClass::AcceptInvite()
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    FJoinLobby::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LocalName = FName("InvitedLobby");
    Params.LobbyId = PendingInvitation->LobbyId;

    Lobbies->JoinLobby(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FJoinLobby>& Result) {});
}

void UMyClass::DeclineInvite()
{
    ILobbiesPtr Lobbies = GetLobbiesInterface();

    FDeclineLobbyInvitation::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LobbyId = PendingInvitation->LobbyId;

    Lobbies->DeclineLobbyInvitation(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FDeclineLobbyInvitation>& Result) {});
}
void UMyClass::SetupInviteHandling()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    // Platform overlay handles invite acceptance automatically via this delegate
    Sessions->AddOnSessionUserInviteAcceptedDelegate_Handle(
        FOnSessionUserInviteAcceptedDelegate::CreateUObject(
            this, &UMyClass::HandleInviteAccepted));
}

void UMyClass::HandleInviteAccepted(bool bWasSuccessful, int32 ControllerId,
    FUniqueNetIdPtr UserId, const FOnlineSessionSearchResult& InviteResult)
{
    if (bWasSuccessful && InviteResult.IsValid())
    {
        // Store for UI confirmation or join directly
        PendingInviteResult = InviteResult;
        ShowInviteConfirmationUI();
    }
}

void UMyClass::AcceptInvite()
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    Sessions->AddOnJoinSessionCompleteDelegate_Handle(
        FOnJoinSessionCompleteDelegate::CreateLambda(
            [this](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
            {
                if (Result == EOnJoinSessionCompleteResult::Success)
                {
                    UE_LOG(LogTemp, Log, TEXT("Accepted invite, joined lobby!"));
                }
            }));

    Sessions->JoinSession(0, FName("InvitedLobby"), PendingInviteResult);
}

void UMyClass::DeclineInvite()
{
    // Clear the pending invite - no API call needed for OSSv1
    PendingInviteResult = FOnlineSessionSearchResult();
    UE_LOG(LogTemp, Log, TEXT("Declined invite"));
}

Member Attributes

Each player can set their own attributes (team, ready status, character):

Use Update Lobby Member Attributes to change your own data.

Update Lobby Member Attributes node
Copy and paste into Unreal Engine Blueprint editor
void UMyClass::SetReady(bool bReady)
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();
    FEIKLobby Lobby = Lobbies->GetLobbyByName(FName("GameLobby"));

    TMap<FString, FEIKLobbyAttribute> Attrs;
    Attrs.Add(TEXT("Ready"), FEIKLobbyAttribute::Bool(bReady));
    Attrs.Add(TEXT("Character"), FEIKLobbyAttribute::String(TEXT("Warrior")));

    Lobbies->ModifyMemberAttributes(0, Lobby.LobbyId, Attrs, {},
        FEIKAsyncCallbackVoid::CreateLambda([](const TEIKAsyncResult<void>& Result) {}));
}
void UMyClass::SetReady(bool bReady)
{
    IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
    ILobbiesPtr Lobbies = Services->GetLobbiesInterface();

    TSharedPtr<const FLobby> Lobby = GetCurrentLobby();
    if (!Lobby.IsValid()) return;

    FModifyLobbyMemberAttributes::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LobbyId = Lobby->LobbyId;
    Params.UpdatedAttributes.Add(FSchemaAttributeId(TEXT("Ready")), FSchemaVariant(bReady));
    Params.UpdatedAttributes.Add(FSchemaAttributeId(TEXT("Character")), FSchemaVariant(TEXT("Warrior")));

    Lobbies->ModifyLobbyMemberAttributes(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FModifyLobbyMemberAttributes>& Result) {});
}
void UMyClass::SetReady(bool bReady)
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    FOnlineSessionSettings* Settings = Sessions->GetSessionSettings(FName("GameLobby"));
    if (!Settings) return;

    Settings->Set(FName(TEXT("Ready")), bReady, EOnlineDataAdvertisementType::ViaOnlineService);
    Settings->Set(FName(TEXT("Character")), FString(TEXT("Warrior")), EOnlineDataAdvertisementType::ViaOnlineService);

    Sessions->UpdateSession(FName("GameLobby"), *Settings);
}

Owner Actions

Only the lobby owner can perform these actions:

ActionBlueprint NodeDescription
Update LobbyUpdate EIK LobbyChange lobby attributes or join policy
Kick MemberKick Lobby MemberRemove a player
Promote MemberPromote Lobby MemberTransfer ownership
Destroy LobbyDestroy EIK LobbyClose the lobby for everyone

Host Migration

When enabled (bEnableHostMigration = true), if the lobby owner disconnects:

  1. EOS automatically promotes another member to owner
  2. OnOwnerChanged event fires with the new owner
  3. The lobby continues without interruption

If host migration is disabled, the lobby is destroyed when the owner leaves.

Join by ID (Party Codes)

Players can join directly using the lobby ID (party code). Enable Enable Join By Id in lobby settings when creating.

Find Lobby By ID and Create Lobby with settings
Copy and paste into Unreal Engine Blueprint editor
  1. Enable Enable Join By Id in lobby settings when creating
  2. Use Find Lobby By ID with the lobby code to get the lobby
  3. Use Join EIK Lobby with the result
void UMyClass::JoinByCode(const FString& LobbyCode)
{
    TSharedPtr<IEIKLobbies> Lobbies = GetLobbiesInterface();

    TMap<FString, FEIKLobbyAttribute> MemberAttrs;
    Lobbies->JoinLobbyById(0, FName("GameLobby"), FEIKLobbyId::FromString(LobbyCode), MemberAttrs,
        FEIKAsyncCallback<FEIKJoinLobbyResult>::CreateLambda(
            [](const TEIKAsyncResult<FEIKJoinLobbyResult>& Result)
            {
                if (Result.IsSuccessful())
                {
                    UE_LOG(LogTemp, Log, TEXT("Joined via code!"));
                }
            }));
}
void UMyClass::JoinByCode(const FString& LobbyCode)
{
    IOnlineServicesPtr Services = GetServices(EOnlineServices::Epic);
    ILobbiesPtr Lobbies = Services->GetLobbiesInterface();

    FJoinLobby::Params Params;
    Params.LocalAccountId = GetLocalAccountId();
    Params.LocalName = FName("GameLobby");
    Params.LobbyId = FLobbyId(LobbyCode);

    Lobbies->JoinLobby(MoveTemp(Params))
        .OnComplete([](const TOnlineResult<FJoinLobby>& Result)
        {
            if (Result.IsOk())
            {
                UE_LOG(LogTemp, Log, TEXT("Joined via code!"));
            }
        });
}
void UMyClass::JoinByCode(const FString& LobbyCode)
{
    IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get(FName(TEXT("EIK")));
    IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();

    // First find the lobby by ID
    TSharedRef<FOnlineSessionSearch> SearchSettings = MakeShared<FOnlineSessionSearch>();
    SearchSettings->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
    SearchSettings->QuerySettings.Set(FName(TEXT("LobbyId")), LobbyCode, EOnlineComparisonOp::Equals);

    Sessions->AddOnFindSessionsCompleteDelegate_Handle(
        FOnFindSessionsCompleteDelegate::CreateLambda(
            [this, Sessions, SearchSettings](bool bWasSuccessful)
            {
                if (bWasSuccessful && SearchSettings->SearchResults.Num() > 0)
                {
                    Sessions->JoinSession(0, FName("GameLobby"), SearchSettings->SearchResults[0]);
                }
            }));

    Sessions->FindSessions(0, SearchSettings);
}

Voice Chat

Enable bEnableVoiceChat when creating the lobby to automatically set up an RTC room. See Voice Chat for mute/volume controls.

Common Patterns

Ready Check

bool UMyClass::AreAllPlayersReady(const FEIKLobby& Lobby)
{
    for (const FEIKLobbyMember& Member : Lobby.Members)
    {
        if (Member.Attributes.Contains(TEXT("Ready")))
        {
            if (!Member.Attributes[TEXT("Ready")].BoolValue)
            {
                return false;
            }
        }
        else
        {
            return false; // No ready status set
        }
    }
    return Lobby.Members.Num() > 0;
}

Start Game When Ready

void UMyClass::CheckAndStartGame()
{
    FEIKLobby Lobby = Lobbies->GetLobbyByName(FName("GameLobby"));

    if (AreAllPlayersReady(Lobby) && Lobby.GetMemberCount() >= MinPlayers)
    {
        // Owner starts the game
        if (Lobby.OwnerAccountId == GetLocalAccountId())
        {
            ServerTravel(TEXT("/Game/Maps/GameMap"));
        }
    }
}

On this page