I’m trying to get a basic multi-player setup working with UE and Steam. I am using UE 5.8 with C++, Online Subsystem, Steam Sockets and Seamless Travel. I’ve started with the third person shooter project, created three maps (LVL_Title, LVL_Transition, LVL_Lobby), enabled seamless travel in the game mode, created my own multiplayer session subsystem, added the maps to the packaged build list, and setup the maps in the project settings. I’m packaging the application when I test, I have 2 machines each logged into seperate steam accounts set to the same download region, and I am creating my own custom session name when I create the session.
I create the session in my subsystem by calling ServerTravel passing the map location to the lobby level and passing the listen parameter. This works because I then load the lobby level, travel to that level and my character can move around. Below is the code I use to setup the server:
void UMultiPlayerSessionsSubsystem::CreateSession(int32 NumPublicConnections, const FString& MatchType)
{
if (!SessionInterface.IsValid()) return;
if (auto ExistingSession = SessionInterface->GetNamedSession("Brandon_Test"); ExistingSession != nullptr)
{
SessionInterface->DestroySession("Brandon_Test");
}
// Store the delegate in a Handle so we can remove it later from the delegate list
CreateSessionCompleteDelegateHandle = SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(
CreateSessionCompleteDelegate);
LastSessionSettings = MakeShareable(new FOnlineSessionSettings());
LastSessionSettings->bIsLANMatch = Online::GetSubsystem(GetWorld())->GetSubsystemName() == "NULL";
LastSessionSettings->NumPublicConnections = NumPublicConnections;
LastSessionSettings->bAllowJoinInProgress = true;
LastSessionSettings->bAllowJoinViaPresence = true;
LastSessionSettings->bShouldAdvertise = true;
LastSessionSettings->bUsesPresence = true;
LastSessionSettings->bUseLobbiesIfAvailable = true;
LastSessionSettings->Set(FName("MatchType"), MatchType, EOnlineDataAdvertisementType::ViaOnlineServiceAndPing);
LastSessionSettings->BuildUniqueId = 1;
if (const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); !SessionInterface->
CreateSession(*LocalPlayer->GetPreferredUniqueNetId(), "Brandon_Test", *LastSessionSettings))
{
SessionInterface->ClearOnCreateSessionCompleteDelegate_Handle(CreateSessionCompleteDelegateHandle);
// broadcast our own custom delegate
MultiPlayerOnCreateSessionComplete.Broadcast(false);
}
}
After this is called the following code runs where ServerTravel is called with the listen parameter:
void UMenu::OnCreateSession(const bool bWasSuccessful)
{
if (bWasSuccessful)
{
if (UWorld* World = GetWorld())
{
World->ServerTravel("/Game/Maps/LVL_Lobby?listen");
}
}
else
{
if (GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::Red, TEXT("Failed to create session"));
}
}
}
After this my first machine is connected to the lobby level and is waiting for a connection from another client. After that I have a couple of functions that call find sessions and join sessions which are both listed below:
void UMultiPlayerSessionsSubsystem::FindSessions(const int32 MaxSearchResults)
{
if (!SessionInterface.IsValid()) return;
FindSessionsCompleteDelegateHandle = SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegate);
LastSessionSearch = MakeShareable(new FOnlineSessionSearch());
LastSessionSearch->MaxSearchResults = MaxSearchResults;
LastSessionSearch->bIsLanQuery = Online::GetSubsystem(GetWorld())->GetSubsystemName() == "NULL";
LastSessionSearch->QuerySettings.Set(SEARCH_LOBBIES, true, EOnlineComparisonOp::Equals);
if (const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); !SessionInterface->
FindSessions(*LocalPlayer->GetPreferredUniqueNetId(), LastSessionSearch.ToSharedRef()))
{
SessionInterface->ClearOnFindSessionsCompleteDelegate_Handle(FindSessionsCompleteDelegateHandle);
MultiPlayerOnFindSessionsComplete.Broadcast(TArray<FOnlineSessionSearchResult>(), false);
}
}
Since I’m using a custom name for my session only 1 item is ever found here and it is sent to the following method:
void UMenu::OnFindSessions(const TArray<FOnlineSessionSearchResult>& SessionResults, bool bWasSuccessful)
{
if (MultiPlayerSessionsSubsystem == nullptr) return;
for (auto Result : SessionResults)
{
FString SettingsValue;
Result.Session.SessionSettings.Get(FName("MatchType"), SettingsValue);
if (SettingsValue == MatchType)
{
MultiPlayerSessionsSubsystem->JoinSession(Result);
return;
}
}
}
Here JoinSession is called on the subsystem which runs this code, the code setting bUseLobbiesIfAvailable and bUsesPresence came from a suggestion I found in this forum as a way to potentially fix the problem I’m having:
void UMultiPlayerSessionsSubsystem::JoinSession(const FOnlineSessionSearchResult& SessionResult)
{
if (!SessionInterface.IsValid())
{
MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
return;
}
JoinSessionCompleteDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegate);
FOnlineSessionSearchResult& NewSession = const_cast<FOnlineSessionSearchResult&>(SessionResult);
FOnlineSessionSettings& SessionSettings = NewSession.Session.SessionSettings;
SessionSettings.bUseLobbiesIfAvailable = true;
SessionSettings.bUsesPresence = true;
if (const ULocalPlayer* LocalPlayer = GetWorld()->GetFirstLocalPlayerFromController(); !SessionInterface->
JoinSession(*LocalPlayer->GetPreferredUniqueNetId(), "Brandon_Test", SessionResult))
{
SessionInterface->ClearOnJoinSessionCompleteDelegate_Handle(JoinSessionCompleteDelegateHandle);
MultiPlayerOnJoinSessionComplete.Broadcast(EOnJoinSessionCompleteResult::UnknownError);
}
}
After this I finally call ClientTravel with the address retrieved from Steam using the online subsystem:
void UMenu::OnJoinSession(const EOnJoinSessionCompleteResult::Type Result)
{
if (const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld()))
{
const IOnlineSessionPtr SessionInterface = Subsystem->GetSessionInterface();
if (SessionInterface.IsValid())
{
FString Address;
SessionInterface->GetResolvedConnectString("Brandon_Test", Address);
if (APlayerController* PlayerController = GetGameInstance()->GetFirstLocalPlayerController())
{
PlayerController->ClientTravel(Address, TRAVEL_Relative, true);
UE_LOG(LogOnlineSession, Log, TEXT("Called client travel to address: %s"), *Address);
}
}
}
}
Initially I thought ClientTravel was throwing an error and just returning to the title map, but if you look through the logs you’ll see that it purposefully loads the title map and never attempts to call the lobby map (I removed the actual ID in the steam address in the logs).
[2026.07.02-13.02.29:706][232]LogOnlineSession: Warning: ******JOINSESSION Brandon_Test SUCCESSFUL******
[2026.07.02-13.02.29:706][232]LogOnlineSession: STEAM: Using P2P Data for Connection Serialization
[2026.07.02-13.02.29:708][232]LogWorld: SeamlessTravel to: /Game/Maps/LVL_Title
[2026.07.02-13.02.29:710][232]LogOnlineSession: Called client travel to address: steam.REDACTED:7777
[2026.07.02-13.02.29:781][235]LogWorld: BeginTearingDown for /Game/Maps/LVL_Title
[2026.07.02-13.02.29:783][235]LogViewport: Display: Viewport MouseLockMode Changed, DoNotLock -> LockOnCapture
[2026.07.02-13.02.29:783][235]LogViewport: Display: Viewport MouseCaptureMode Changed, NoCapture -> CapturePermanently
[2026.07.02-13.02.29:783][235]LogViewport: Display: Player bShowMouseCursor Changed, True -> False
[2026.07.02-13.02.29:793][235]LogWorld: UWorld::CleanupWorld for LVL_Title, bSessionEnded=false, bCleanupResources=true
[2026.07.02-13.02.29:796][235]LogSlate: InvalidateAllWidgets triggered. All widgets were invalidated
[2026.07.02-13.02.29:802][235]LogAudio: Display: Audio Device (ID: 1) registered with world 'LVL_Transition'.
[2026.07.02-13.02.29:826][235]LogAudio: Display: Audio Device unregistered from world 'LVL_Title'.
[2026.07.02-13.02.29:832][235]LogRenderer: Forcing update for all mesh draw commands: SkyLight real-time capture change
[2026.07.02-13.02.29:847][235]LogUObjectHash: Compacting FUObjectHashTables data took 0.48ms
[2026.07.02-13.02.29:848][235]LogChaosDD: Creating Chaos Debug Draw Scene for world LVL_Transition
[2026.07.02-13.02.29:850][235]LogStreaming: Display: FlushAsyncLoading(81): 0 QueuedPackages, 1 AsyncPackages
[2026.07.02-13.02.29:860][235]LogStats: SeamlessTravel FlushLevelStreaming - 0.000 s
[2026.07.02-13.02.29:861][235]LogWorld: Bringing World /Game/Maps/LVL_Transition.LVL_Transition up for play (max tick rate 0) at 2026.07.02-23.02.29
[2026.07.02-13.02.29:861][235]LogWorld: Bringing up level for play took: 0.000685
[2026.07.02-13.02.29:861][235]LogWorld: Sending NotifyLoadedWorld for LP: LocalPlayer_2147482468 PC: BP_ThirdPersonPlayerController_C_2147482391
[2026.07.02-13.02.29:861][235]LogNet: Verbose: NotifyLoadedWorld Begin
[2026.07.02-13.02.29:861][235]LogNet: Verbose: NotifyLoadedWorld End
[2026.07.02-13.02.29:861][235]LogWorld: StartLoadingDestination to: /Game/Maps/LVL_Title
[2026.07.02-13.02.29:862][235]LogAppleController: [HandleMouseConnected] Handling physical mouse connection...
[2026.07.02-13.02.29:862][235]LogAppleController: [HandleMouseConnected] Handling physical mouse connection...
[2026.07.02-13.02.29:862][235]LogAppleController: [HandleMouseConnected] Handling physical mouse connection...
[2026.07.02-13.02.29:917][241]LogWorld: BeginTearingDown for /Game/Maps/LVL_Transition
[2026.07.02-13.02.29:918][241]LogWorld: UWorld::CleanupWorld for LVL_Transition, bSessionEnded=true, bCleanupResources=true
[2026.07.02-13.02.29:918][241]LogSlate: InvalidateAllWidgets triggered. All widgets were invalidated
[2026.07.02-13.02.29:918][241]LogAudio: Display: Audio Device (ID: 1) registered with world 'LVL_Title'.
[2026.07.02-13.02.29:922][241]LogAudio: Display: Audio Device unregistered from world 'LVL_Transition'.
[2026.07.02-13.02.29:943][241]LogUObjectHash: Compacting FUObjectHashTables data took 0.30ms
[2026.07.02-13.02.29:944][241]LogChaosDD: Creating Chaos Debug Draw Scene for world LVL_Title
[2026.07.02-13.02.29:944][241]LogStreaming: Display: FlushAsyncLoading(83): 1 QueuedPackages, 0 AsyncPackages
[2026.07.02-13.02.29:957][241]LogLoad: Game class is 'BP_ThirdPersonGameMode_C'
[2026.07.02-13.02.29:957][241]LogStats: SeamlessTravel FlushLevelStreaming - 0.000 s
[2026.07.02-13.02.29:958][241]LogWorld: Bringing World /Game/Maps/LVL_Title.LVL_Title up for play (max tick rate 0) at 2026.07.02-23.02.29
[2026.07.02-13.02.29:958][241]LogWorld: Bringing up level for play took: 0.001399
As you can see the third line in the log says its attempting seamless travel to LVL_Title and not LVL_Lobby like it should. The only thing I see that makes me wonder if this could be part of the problem is Steam’s return from find sessions; I’ll only put one pair here, but this doesn’t happen for all of them just a large amount of them.
[2026.07.02-13.02.29:424][224]LogOnlineSession: STEAM: Search result 35: LobbyId=Lobby[0x18600002934D228], LobbyId.IsValid()=true, CSteamID(LobbyId).IsLobby()=true
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Unknown or unsupported data type from Steam key data 199_cce4cdac3a5fc4b9cb216c8f49a0df6b68014861867eecce0584313ed0f96964_6 lobby_password_ykssr_199
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Failed to parse setting from key 199_cce4cdac3a5fc4b9cb216c8f49a0df6b68014861867eecce0584313ed0f96964_6 value lobby_password_ykssr_199
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Unknown or unsupported data type from Steam key data lobby_key 16ca67264987f56ca821525a2f65884cedc2546cf15ca3e69292c5c394c9d4ef
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Failed to parse setting from key lobby_key value 16ca67264987f56ca821525a2f65884cedc2546cf15ca3e69292c5c394c9d4ef
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Unknown or unsupported data type from Steam key data lobby_preferences 00000000000000000000000000010001
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Failed to parse setting from key lobby_preferences value 00000000000000000000000000010001
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Unknown or unsupported data type from Steam key data lobby_type yknx3_seamless_master_lobby
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Failed to parse setting from key lobby_type value yknx3_seamless_master_lobby
[2026.07.02-13.02.29:424][224]LogOnlineSession: Warning: STEAM: Unable to parse search result for lobby 'Lobby[0x18600002934D228]'
I did search online for the last warning message there and that is what has lead me to the place I’m at currently. I know its a lot of information, I’m just hoping that somewhere in there is an obvious mistake I’ve made or something I just haven’t figured out. I would appreciate any advice anyone could give. One last thing…I’m working on a Mac so that might have some bearing on this issue as well. I have dealt with the Mac entitlements issue already, so that shouldn’t be the problem.