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

Parameters:
| Parameter | Description |
|---|---|
| Lobby Name | Local identifier for this lobby (default: "GameLobby") |
| Settings | Lobby 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
| Setting | Type | Default | Description |
|---|---|---|---|
| MaxMembers | int | 10 | Maximum players (2-64) |
| JoinPolicy | enum | PublicAdvertised | Who can find/join |
| bEnableVoiceChat | bool | false | Enable built-in voice chat |
| bEnableHostMigration | bool | true | Auto-promote new host if owner leaves |
| bUsePresence | bool | false | Show in social overlay |
| bAllowInvites | bool | true | Allow sending invites |
| bEnableJoinById | bool | true | Allow direct join via lobby ID |
| Attributes | Map | empty | Custom searchable key-value data |
Join Policies
| Policy | Searchable | Join by ID | Invite Only |
|---|---|---|---|
| PublicAdvertised | Yes | Yes | No |
| PublicNotAdvertised | No | Yes | No |
| InviteOnly | No | No | Yes |
Find Lobbies

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

| Parameter | Description |
|---|---|
| Lobby To Join | The lobby from search results |
| Local Lobby Name | Local identifier for tracking |
| Auto Travel | Automatically 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

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:
- Get the subsystem:
Get Game Instance→Get Subsystem→EIK Lobby Events Subsystem - Bind to events like
On Member Joined,On Lobby Left,On Invite Received

| Event | When Fired |
|---|---|
| On Lobby Joined | Local player joins a lobby |
| On Lobby Left | Local player leaves a lobby |
| On Member Joined | Another player joins |
| On Member Left | Another player leaves |
| On Owner Changed | Host migration occurred |
| On Lobby Attributes Changed | Lobby settings changed |
| On Member Attributes Changed | A member's attributes changed |
| On Lobby Invite Received | Received 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

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
- Bind to
On Lobby Invite Receivedevent on the EIK Lobby Events Subsystem - Show UI with invite details from the event (sender, lobby info)
- Use Accept EIK Lobby Invite or Reject EIK Lobby Invite nodes

| Node | Parameters |
|---|---|
| Accept EIK Lobby Invite | Lobby ID, Local Name, Auto Travel |
| Reject EIK Lobby Invite | Lobby 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.

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:
| Action | Blueprint Node | Description |
|---|---|---|
| Update Lobby | Update EIK Lobby | Change lobby attributes or join policy |
| Kick Member | Kick Lobby Member | Remove a player |
| Promote Member | Promote Lobby Member | Transfer ownership |
| Destroy Lobby | Destroy EIK Lobby | Close the lobby for everyone |
Host Migration
When enabled (bEnableHostMigration = true), if the lobby owner disconnects:
- EOS automatically promotes another member to owner
OnOwnerChangedevent fires with the new owner- 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.

- Enable
Enable Join By Idin lobby settings when creating - Use Find Lobby By ID with the lobby code to get the lobby
- 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"));
}
}
}