メインコンテンツまでスキップ
Last updated on November 10, 2025

Set Up Matchmaking Using OSS

To implement matchmaking into your game, follow these steps:

Prerequisites

  • Enable Sessions V2 in the AccelByte Online Subsystem (OSS) by adding the following to the DefaultEngine.ini file:

    [OnlineSubsystemAccelByte]
    bEnableV2Sessions=true
  • Enable AccelByteNetworkUtilities by adding the following to the DefaultEngine.ini

    [AccelByteNetworkUtilities]
    UseTurnManager=true
    HostCheckTimeout=5

    [/Script/AccelByteNetworkUtilities.IpNetDriverAccelByte]
    NetConnectionClassName=AccelByteNetworkUtilities.IpConnectionAccelByte
    AllowDownloads=false

    [/Script/Engine.Engine]
    !NetDriverDefinitions=ClearArray
    +NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="/Script/AccelByteNetworkUtilities.IpNetDriverAccelByte",DriverClassNameFallback="/Script/OnlineSubsystemUtils.IpNetDriver")
    +NetDriverDefinitions=(DefName="DemoNetDriver",DriverClassName="/Script/Engine.DemoNetDriver",DriverClassNameFallback="/Script/Engine.DemoNetDriver")
  • Add the required permissions for Matchmaking and Session V2 to your Game Client in the Admin Portal.

  • Your Game Client must have authenticated with the AccelByte backend and connected to the Lobby service.

Start matchmaking

In the function where you plan to initiate matchmaking from, you need to acquire the Session Interface from the AccelByte OSS. Note that there is a custom AccelByte Session Interface with the FOnlineSessionAccelBytePtr type that you will be using to request matchmaking, .

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

const FOnlineSessionAccelBytePtr SessionInterface = StaticCastSharedPtr<FOnlineSessionV2AccelByte>(Subsystem->GetSessionInterface());
if (!ensure(SessionInterface.IsValid()))
{
return;
}

Make sure to obtain the Player ID for the Game Client, as you'll need it later to include the Player Controller ID in the matchmaking request. The Player Controller ID will also be used when handling callback results.

const FUniqueNetIdPtr LocalPlayerId = GetUniquePlayerId();
if (!ensure(LocalPlayerId.IsValid()))
{
return false;
}

Create a Session Search Handle FOnlineSessionSearch to be used to store the session result created during the matchmaking process if a match is found successfully. When creating a Session Search Handle, set the query settings with the following parameters:

  • Player Controller ID: the OSS ID of the player for which the match ticket will be submitted during matchmaking.
  • Match Pool Session Settings: the OSS session settings that will be used during matchmaking SETTING_SESSION_MATCHPOOL. By passing this in, it will be updated with the Match Pool Name you provide as the next parameter.
  • Match Pool Name: the name of the match pool you defined as part of the prerequisites for the specified game mode. This will inform the Matchmaking service to add the newly created match ticket to that match pool for evaluation.
  • Comparison Operator: the OSS operator to be used as part of the session search, which in this case is EOnlineComparisonOp::Equals

You need to register a FOnMatchmakingCompleteDelegate callback delegate to listen for the match results, which will trigger when matchmaking completes. Refer to the Sample callback function section for a sample code.

  • Session Name: the name of the game session created if there was a successful match.
  • Success Value: a boolean value indicating whether matchmaking successfully found a match.
// Bind a function that has a return type of void and these parameters:
// FName SessionName, bool bWasSuccessful
const FOnMatchmakingCompleteDelegate OnMatchmakingCompleteDelegate = /* Bind to lambda or class method */;
SessionInterface->AddOnMatchmakingCompleteDelegate_Handle(OnMatchmakingCompleteDelegate);

Once you have the above, you can call StartMatchmaking() from the Session Interface to request matchmaking to start. This function takes the following parameters:

  • Player Controller ID: the OSS ID of the player that the Match Ticket will be submitted for during matchmaking.

  • Session Name: the name of the session created if matchmaking is successful. Typically, you will pass NAME_GameSession.

  • Session Settings: the session settings that should be used by the Session service to create the Game Session, FOnlineSessionSettings(). In this case, you will leave it up to the Session service to assign the settings by passing in an empty object.

  • Session Search Handle: the Session Search Handle you created earlier in this section to store the Game Session that will be created if matchmaking is successful.

Once the request to StartMatchmaking() is made, assuming the call returns true, you should bind the Session Search Handle you defined earlier in this section to a class member for future access. If StartMatchmaking() returns false, that indicates that there was an issue calling into the Matchmaking service.

if (SessionInterface->StartMatchmaking(USER_ID_TO_MATCHMAKING_USER_ARRAY(LocalPlayerId.ToSharedRef()), NAME_GameSession, FOnlineSessionSettings(), MatchmakingSearchHandle, OnStartMatchmakingCompleteDelegate))
{
// Update the current search result handle class member with the search handle passed to the matchmaking service.
CurrentMatchmakingSearchHandle = MatchmakingSearchHandle;
}

Start matchmaking with Turn Server QoS

備考

This feature is only available in AGS version 3.75.

The implementation automatically handles the low-level tasks to obtain the Turn Server QoS before creating a match ticket and providing latency information.

To access the custom function of SetIsP2PMatchmaking, first specify a new matchmaking search handle using the FOnlineSessionSearchAccelByte class. Then, set the parameter to true to indicate that the match is for a P2P session. Finally, pass the matchmaking search handle into the StartMatchmaking function to begin the match.

TSharedRef<FOnlineSessionSearchAccelByte> MatchmakingSearchHandle = MakeShared<FOnlineSessionSearchAccelByte>();
MatchmakingSearchHandle->QuerySettings.Set(SETTING_SESSION_MATCHPOOL, MatchPoolName, EOnlineComparisonOp::Equals);
MatchmakingSearchHandle->SetIsP2PMatchmaking(true);

if (SessionInterface_User1->StartMatchmaking(USER_ID_TO_MATCHMAKING_USER_ARRAY(LocalUserId1.ToSharedRef()), NAME_GameSession, FOnlineSessionSettings(), MatchmakingSearchHandle, OnStartMatchmakingCompleteDelegate))
{
CurrentMatchmakingSearchHandle = MatchmakingSearchHandle;
}

Join game session and travel using P2P connection

Once matchmaking completes, the OnMatchmakingCompleteDelegate you bound prior to starting matchmaking will fire and you can process the results by following these instructions.

Retrieve the game session result stored in the SearchResults member array of the Session Search Handle. If the array is empty, refer to Troubleshooting.

// Ensure that we have a valid session search result in the array before continuing
if (!CurrentMatchmakingSearchHandle->SearchResults.IsValidIndex(0))
{
return false;
}

FOnlineSessionSearchResult MatchResult = CurrentMatchmakingSearchHandle->SearchResults[0];
EOnlineSessionTypeAccelByte SessionType = SessionInterface->GetSessionTypeFromSettings(MatchResult.Session.SessionSettings);
if (SessionType != EOnlineSessionTypeAccelByte::GameSession)
{
return false;
}

Check if the player is already in a game session, if so, you will need to destroy it so they can join the new session returned through matchmaking. You can register a FOnDestroySessionCompleteDelegate callback delegate to listen for the result.

// Check if we already have a game session, if so, destroy it to join this one
if (SessionInterface->GetNamedSession(NAME_GameSession) != nullptr)
{
const FOnDestroySessionCompleteDelegate OnDestroySessionForJoinCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &UOSSDemoGameSessionSubsystem::OnDestroySessionForJoinComplete, Session);
return SessionInterface->DestroySession(NAME_GameSession, OnDestroySessionForJoinCompleteDelegate);
}

Register a FOnJoinSessionCompleteDelegate callback delegate to listen for the join result, which will trigger when the join request completes. In this delegate you will also need to handle P2P connection. You need to check if the user is a game session leader or not. If the user is a game session leader (first user to join game session) then you need to set this user as listen server. If the user is regular member they should initiate connection to game session leader.

// Register a delegate for joining the specified session
const FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &UOSSDemoGameSessionSubsystem::OnJoinSessionComplete);
JoinSessionDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);

Here is the sample of OnJoinSessionCompleteDelegate for handling P2P connection

void UOSSDemoGameSessionSubsystem::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
// Ignore non-game session join results
if (SessionName != NAME_GameSession)
{
return;
}

if (Result != EOnJoinSessionCompleteResult::Success)
{
return;
}

const FOnlineSessionAccelBytePtr SessionInterface = GetSessionInterface();
ensure(SessionInterface.IsValid());

FNamedOnlineSession* Session = SessionInterface->GetNamedSession(SessionName);
if (!ensure(Session != nullptr))
{
return;
}
TSharedPtr<FOnlineSessionInfoAccelByteV2> SessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(Session->SessionInfo);

if (!ensure(SessionInfo.IsValid()))
{
return;
}

ULocalPlayer* LocalPlayer = GetLocalPlayer();
if (!ensure(LocalPlayer != nullptr))
{
return;
}

APlayerController* Controller = LocalPlayer->GetPlayerController(GetWorld());
if (!ensure(Controller != nullptr))
{
return;
}

// If the server type for the created session is either NONE (local), then the connection info is in the session attributes
const EAccelByteV2SessionConfigurationServerType ServerType = SessionInfo->GetServerType();
if (ServerType != EAccelByteV2SessionConfigurationServerType::P2P)
{
// this is not a P2P session
return;
}

FString TravelUrl{};
if (SessionInterface->GetResolvedConnectString(SessionName, TravelUrl, NAME_GamePort) && !TravelUrl.IsEmpty())
{
if(SessionInterface->IsPlayerP2PHost(LocalPlayerId, SessionName))
{
// Travel as listen server
FString MapName;
Session->SessionSettings.Get(SETTING_MAPNAME, MapName);
Controller->ClientTravel(FString::Printf(TEXT("%s?listen"), *MapName), TRAVEL_Absolute);
}
else
{
Controller->ClientTravel(TravelUrl, TRAVEL_Absolute);
}
}
}

Once you have the above, you can call JoinSession() from the Session Interface to join the returned session. This function takes the following parameters:

  • Player Controller ID: the OSS ID of the player for which the match ticket is submitted during matchmaking.

  • Session Name: the name of the game session created if the matchmaking is successful.

  • Session: the OSS Session object to which the the Session service will assign the game session info for later access.

SessionInterface->JoinSession(LocalPlayerId.ToSharedRef().Get(), NAME_GameSession, Session);

Sample callback function

The following is a full example of a callback function that can use as the delegate to listen for OnMatchmakingComplete results:

void OnMatchmakingCompleteDelegate(FName SessionName, bool bWasSuccessful)
{
if (SessionName != NAME_GameSession)
{
return;
}

EOnlineSessionTypeAccelByte SessionType = SessionInterface->GetSessionTypeFromSettings(MatchResult.Session.SessionSettings);
if (SessionType != EOnlineSessionTypeAccelByte::GameSession)
{
return false;
}

// Ensure that we have a valid session search result in the array before continuing
if (!CurrentMatchmakingSearchHandle->SearchResults.IsValidIndex(0))
{
return false;
}

FOnlineSessionSearchResult MatchResult = CurrentMatchmakingSearchHandle->SearchResults[0];

// Check if we already have a game session that we are in, if so, destroy it to join this one
if (SessionInterface->GetNamedSession(NAME_GameSession) != nullptr)
{
const FOnDestroySessionCompleteDelegate OnDestroySessionForJoinCompleteDelegate = FOnDestroySessionCompleteDelegate::CreateUObject(this, &UOSSDemoGameSessionSubsystem::OnDestroySessionForJoinComplete, Session);
return SessionInterface->DestroySession(NAME_GameSession, OnDestroySessionForJoinCompleteDelegate);
}

// Register a delegate for joining the specified session
const FOnJoinSessionCompleteDelegate OnJoinSessionCompleteDelegate = FOnJoinSessionCompleteDelegate::CreateUObject(this, &UOSSDemoGameSessionSubsystem::OnJoinSessionComplete);


JoinSessionDelegateHandle = SessionInterface->AddOnJoinSessionCompleteDelegate_Handle(OnJoinSessionCompleteDelegate);

const FUniqueNetIdPtr LocalPlayerId = GetUniquePlayerId();
if (!ensure(LocalPlayerId.IsValid()))
{
return false;
}

return SessionInterface->JoinSession(LocalPlayerId.ToSharedRef().Get(), NAME_GameSession, Session);
}
ヒント

For debugging purposes, use the Relay (TURN) as the Interactive Connectivity Establishment (ICE) connection by passing the additional parameter, -iceforcerelay, during the game launch.