Online subsystem dedicated server NAT traversal

Hello, I am trying to figure out how to make my idea work with the help of the online subsystem.

The idea is as follows:
A player hosts a game by starting an instance of an unreal engine dedicated server that runs locally. This dedicated server then creates a session which all the clients (including the “host”) should join.
I am aware that the player could just run a the server as a listen server, but this is not what i want. I want each client to have more or less the same way of communicating with the server.

Most of it works: session is created, session can be discovered and joined by players etc.
But when they try to ClientTravel to the connection string, something like this:

				FString ConnectString;
				if (GetSessionPtr()->GetResolvedConnectString(SessionName, ConnectString))
				{
					// Travel to the session
					if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
					{
						PC->ClientTravel(ConnectString, ETravelType::TRAVEL_Absolute);
					}
				}

The correct, (although raw) ip:port is returned and no NAT traversal seems to be happening as the connection times out.

Currently I am using the Epic Online Services and I was under the impression that it should handle all the NAT stuff for me.

Was i wrong, does EOS not handle NAT?
Will steam be able to do this for me?

Am i approaching this wrong?

Any help will be appreciated.

Have a good day :slight_smile:

hello.
you should use joinSession in the onlinesubsystem, not connect by ip.
i don’t remember exactly, but im quite positive i havent done a cilent travel. just when joining the session it opened the map automatically.
i don’t have the code in front of me though.

afaik eos handles nat, but i’m not aware of the limitations or requirements. have you checked the documentation? if so what exactly you need help with.

i’ve heard steam also does nat traversal, but i’m confident it’d be the same workflow. so i wouldn’t change to steam for that.

have a good day too :slight_smile:

If you’re using EOS, you should not be connecting by IP, it doesn’t handle NAT traversal that way.
It’s the same with Steam. If you use the Steam Online Subsystem, create a session (lobby), and connect to that, it will guarantee connection. If you’re joining by IP, you’re on your own (although with Steam, I dont think you can join to IP anyways)

Thanks for the replies, I so appreciate it, as I keep trying to wrap my head around it.

Both of you must be right in that the raw ip is not correct. What i cannot figure out is: how to get the connection string to be the correct “token”, or whatever else EOS uses, to be able make the proper connection.

Just to clarify, the ClientTravel and reading of the connection string happens in the JoinSessionComplete delegate.
I now realise that I have shared way too little of the code, so here is the rest of the session joining logic:

void USessionManagerComponent::FindSessionById(const FString& SessionIdStr)
{
	TSharedPtr<const FUniqueNetId> LocalUserId = IOnlineSubsystem::Get()->GetIdentityInterface()->GetUniquePlayerId(0);
	if (!LocalUserId.IsValid())
	{
		UE_LOG(LogTemp, Warning, TEXT("No local user id."));
		return;
	}

	// Convert the string SessionIdStr into a FUniqueNetId
	FUniqueNetIdPtr SessionId = GetSessionPtr()->CreateSessionIdFromString(SessionIdStr);
	if (!SessionId.IsValid())
	{
		UE_LOG(LogTemp, Warning, TEXT("Failed to create session ID from string: %s"), *SessionIdStr);
		return;
	}
	UE_LOG(LogTemp, Warning, TEXT("Searching for session with id: %s"), *SessionIdStr);

	FOnSingleSessionResultCompleteDelegate CompletionDelegate;
	CompletionDelegate.BindLambda([this, LocalUserId](int32 LocalPlayerNum, bool bWasSuccessful, const FOnlineSessionSearchResult& SearchResult)
	{
		UE_LOG(LogTemp, Warning, TEXT("FindSessionById complete: %s"), bWasSuccessful ? TEXT("SUCCESS") : TEXT("FAILURE"));

		if (bWasSuccessful && SearchResult.IsValid())
		{
			UE_LOG(LogTemp, Warning, TEXT("Found session owned by %s"), *SearchResult.Session.OwningUserName);

			GetSessionPtr()->OnJoinSessionCompleteDelegates.AddLambda([this](FName SessionName, EOnJoinSessionCompleteResult::Type Result)
			{

				GetSessionPtr()->OnJoinSessionCompleteDelegates.Clear();
				UE_LOG(LogTemp, Warning, TEXT("JoinSession complete: %d"), static_cast<int32>(Result));

				FString ConnectString;
				if (GetSessionPtr()->GetResolvedConnectString(SessionName, ConnectString))
				{
					// Travel to the session
					if (APlayerController* PC = GetWorld()->GetFirstPlayerController())
					{
						PC->ClientTravel(ConnectString, ETravelType::TRAVEL_Absolute);
					}
				}
			});
			
			GetSessionPtr()->JoinSession(*LocalUserId, m_SessionName, SearchResult);
		}
	});

	GetSessionPtr()->FindSessionById(*LocalUserId, *SessionId, *LocalUserId, CompletionDelegate);
}

This line: UE_LOG(LogTemp, Warning, TEXT("JoinSession complete: %d"), static_cast<int32>(Result)); prints “JoinSession complete: 0” which is EOnJoinSessionCompleteResult::Type::Success

FindSessionById is called by a separate game instance client that has logged into EOS successfully.
The session id string is copy pasted directly from EOS’s dev portal.

If i understand correctly, for eos to to able to nat traverse, both peers (in this case the player client and the dedicated server) have to be logged in?
If that’s the case, how would the dedicated server be associated with an eos account, i don’t think a dedicated server can log in? =/