Skip to main content

Integrate P2P with session browser (Deprecated)

Last updated on October 24, 2024
info

The session browser is deprecated and replaced by game sessions. For more information, see Peer-To-Peer (P2P) using game session.

Overview

This article covers the basics of hosting, joining, and browsing peer-to-peer sessions using AccelByte Gaming Services (AGS) Online Subsystem (OSS) for Unreal Engine. This allows game clients to connect and play together without the need for a dedicated server. Additionally, a session browser implementation allows players to find and join existing open peer-to-peer (P2P) sessions without having knowledge of the host player.

Prerequisites

To complete the steps in this guide, you need:

  • Knowledge of Unreal Engine, including use of the OSS.
  • The AGS Game SDK, NetworkUtilities, and OnlineSubsystem plugins installed to your project.
  • Access to the AGS Admin Portal and a namespace for your game.
  • A session template created with the type set to P2P and the joinability set to OPEN.

Configure the plugins

Follow these steps to prepare your plugins for P2P session browser integration.

  1. Enable V2 sessions in your DefaultEngine.ini file:

    [OnlineSubsystemAccelByte]
    bEnableV2Sessions=true
  2. Set up the turn server and net driver:

    [AccelByteNetworkUtilities]
    UseTurnManager=true
    TurnServerSecret=<your-turn-secret>

    [/Script/AccelByteNetworkUtilities.IpNetDriverAccelByte]
    NetConnectionClassName=AccelByteNetworkUtilities.IpConnectionAccelByte
  3. Configure the net driver definitions per platform. For instance, with Windows, add the following to your WindowsEngine.ini:

    [/Script/Engine.GameEngine]
    !NetDriverDefinitions=ClearArray
    +NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="AccelByteNetworkUtilities.IpNetDriverAccelByte",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")
    +NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")

Host a P2P Session

Here is an example of creating a session using a session template. First, you'll need to do some setup, then you'll call CreateSession on the OSS session interface.

  1. Get the AGS Session interface:

    const IOnlineSubsystem* Subsystem = Online::GetSubsystem(GetWorld());
    if (!ensure(Subsystem != nullptr))
    {
    return;
    }

    FOnlineSessionV2AccelBytePtr SessionInterface;
    if (!FOnlineSessionV2AccelByte::GetFromSubsystem(Subsystem, SessionInterface))
    {
    return;
    }
  2. Configure the Session settings:

    FOnlineSessionSettings NewSessionSettings;

    // This would be the name of the session template created in the Admin Portal
    NewSessionSettings.Set(SETTING_SESSION_TEMPLATE_NAME, TEXT("P2PSession"));

    // You want the new session to be a game session, as opposed to a party session
    NewSessionSettings.Set(SETTING_SESSION_TYPE, SETTING_SESSION_TYPE_GAME_SESSION);

    // You need some kind of parameter that the session browser can later use
    // to query for sessions
    NewSessionSettings.Set(FName(TEXT("IS_P2P_SESSION")), TEXT("true"));

    // At this point, any other custom settings can be applied. For example, you
    // can add a map name that you'll later use when you're hosting the P2P session
    NewSessionSettings.Set(SETTING_MAPNAME, TEXT("MapName"));

  3. Make the call to actually create the session:

    // You create a delegate which will be triggered when the session creation is
    // complete, inside of which you'll call StartSession and perform a travel
    const FOnCreateSessionCompleteDelegate OnCreateSessionCompleteDelegate =
    FOnCreateSessionCompleteDelegate::CreateUObject(this,
    &MyClass::OnCreateSessionComplete);
    FDelegateHandle CreateSessionDelegateHandle =
    SessionInterface->AddOnCreateSessionCompleteDelegate_Handle(
    OnCreateSessionCompleteDelegate);
    // PlayerId is an FUniqueNetIdPtr for the player
    SessionInterface->CreateSession(
    PlayerId.ToSharedRef().Get(), NAME_GameSession, NewSessionSettings);

    In the above code, a handler for the session creation complete delegate is added.

  4. Inside the handler that was added in step 3, do the following:

    1. Check that this is the correct session using the SessionName delegate parameter:

      if(SessionName != NAME_GameSession)
      {
      return;
      }
      tip

      Make sure you check the above for any of the delegate handlers that receive a session name as a parameter.

    2. Get the session interface again. After that, you can grab the actual session instance and mark the session as started:

      FNamedOnlineSession* Session = SessionInterface->GetNamedSession(SessionName);
      if (!ensure(Session != nullptr))
      {
      return;
      }

      SessionInterface->StartSession(SessionName);
    3. Construct a travel URL and perform the travel:

      // You'll use the previously-set map name for traveling
      FString MapName;
      Session->SessionSettings.Get(SETTING_MAPNAME, MapName);

      // Constructing the travel URL with "?listen" appended to the map name so that you host a listen server
      const FString TravelUrl = FString::Printf(TEXT("%s?listen"), *MapName);

      // PlayerController is a pointer to an APlayerController for the local player
      Controller->ClientTravel(TravelUrl, TRAVEL_Absolute);
      tip

      Generally, it can be helpful to add in a session setting indicating whether the session is ready for other players to join, which would be set to some indicative value after the map loads.

Browse and join P2P sessions

In order to browse for P2P sessions, you're going to use the Session interface's FindSessions method with the IS_P2P_SESSION setting from the previous step. To begin, you'll need to again grab the AGS Session interface. Then, you'll set up the attributes you want to query with:

TSharedPtr<FOnlineSessionSearch> QuerySessionsHandle =
MakeShared<FOnlineSessionSearch>();

// You'll set the maximum search results to some arbitrary value
QuerySessionsHandle->MaxSearchResults = 100;

// Search for sessions with the P2P setting you used earlier
QuerySessionsHandle->QuerySettings.Set(
FName(TEXT("IS_P2P_SESSION")), TEXT("true"));

// You can also query for other session settings, such as MAPNAME
QuerySessionsHandle->QuerySettings.Set(SETTING_MAPNAME, TEXT("MapName"));
tip

You'll want to keep the FOnlineSessionSearch handle around for use in the FindSessions completion delegate, as it will contain the search results.

Next, you'll make the call to FindSessions:

const FOnFindSessionsCompleteDelegate OnFindSessionsCompleteDelegate = 
FOnFindSessionsCompleteDelegate::CreateUObject(
this, &MyClass::OnFindSessionsComplete);
FDelegateHandle FindSessionsCompleteDelegateHandle =
SessionInterface->AddOnFindSessionsCompleteDelegate_Handle(
OnFindSessionsCompleteDelegate);
// PlayerId is an FUniqueNetIdPtr for the player
SessionInterface->FindSessions(
PlayerId.ToSharedRef().Get(), QuerySessionsHandle.ToSharedRef());

Inside the handler for the OnFindSessionsComplete delegate, you'll have access to an array of FOnlineSessionSearchResult, which could be used to display a list of sessions in a session browser UI. For example, the handler would often pass this array to another delegate and then reset the search handle:

SomeSessionBrowserListingDelegate.Broadcast(QuerySessionsHandle->SearchResults);
QuerySessionsHandle.Reset();

In order to join one of these sessions, the client simply invokes the JoinSession method, passing one of the session search results from the array mentioned above. First, you'd grab the Session interface again, and then you'd perform the join:

const FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate = 
FOnJoinSessionCompleteDelegate::CreateUObject(
this, &MyClass::OnJoinSessionComplete);
FDelegateHandle JoinSessionDelegateHandle =
SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(
OnJoinSessionCompleteDelegate);

// PlayerId is an FUniqueNetIdPtr for the player, and Session is simply an instance of FOnlineSessionSearchResult
return SessionInterface->JoinSession(
PlayerId.ToSharedRef().Get(), NAME_GameSession, Session);
tip

It is generally good practice, before a join, to check if the player is already in a session using GetNamedSession. If so, you'd want to call DestroySession, and then join the session once the delegate for that method is triggered.

Inside the JoinSessionComplete delegate handler, you'd want to then perform a travel. Again, you'd start by grabbing the Session interface, then you'd grab the travel URL and perform the client travel:

FString TravelUrl{};
// SessionName is a parameter from the join delegate
if (SessionInterface->GetResolvedConnectString(SessionName, TravelUrl,
NAME_GamePort) && !TravelUrl.IsEmpty())
{
// PlayerController is a pointer to an APlayerController for the local player
PlayerController->ClientTravel(TravelUrl, TRAVEL_Absolute);
}
note

For P2P sessions, the travel URL will be of the format accelbyte.<host_user_id>:<port>. In this case, the call to fetch the resolved connect string is simply generating the travel URL from the local session information.

Troubleshooting

In this section, you can find common errors and issues that may occur when using this service, along with recommendations on how to resolve them.

Players joining too early

It's possible to encounter an issue where a joining player (non-host) tries to travel to the server before the map has loaded.

Suggestions

A solution for this is to add a session setting (e.g., SETTING_JOIN_READY) which would be set to something like "true" inside a delegate added to FCoreUObjectDelegates::PostLoadMapWithWorld:

FOnlineSessionSettings* SessionSettings = 
SessionInterface->GetSessionSettings(NAME_GameSession);
SessionSettings->Set(FName(TEXT("JOIN_READY")), TEXT("true"));

SessionInterface->UpdateSession(NAME_GameSession, *SessionSettings);

Then, on the joiner side, you'd want to listen to the UpdateReceived delegate on the session interface and look for that session setting before attempting the join. It could also be helpful to add this parameter to the query settings in your session browser's use of FindSessions so that the browser only displays sessions that are ready to be joined.