メインコンテンツまでスキップ

サブシステムを実装する - ピアツーピアで参加可能なセッション - (Unreal Engine モジュール)

Last updated on February 4, 2026

注釈:本資料はAI技術を用いて翻訳されています。

マッチセッションの実装は、2つの異なるクラスを通じて行われます。Online Session クラスと Game Instance Subsystem クラスです。Online Session はゲームクライアントに関連するすべてのロジックを実装する場所であり、Game Instance Subsystem はサーバーロジック用です。

セッション閲覧フロー

始める前に、ピアツーピア(P2P)のセッション閲覧フローがどのように機能するかを理解してください。

ゲーム設定

ピアツーピア(P2P)を有効にするために、AccelByte Network Utilities プラグインを使用しています。P2P が意図したとおりに動作するように設定する必要があるいくつかの設定があります。これらは /Config/DefaultEngine.ini に存在する設定です。

[AccelByteNetworkUtilities]
UseTurnManager=true
bNonSeamlessTravelUseNewConnection=true
HostCheckTimeout=5

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

チュートリアルに従っている場合は、これについて何も変更する必要はありません。これは、このゲームを独自のゲームに統合する場合の情報セクションです。

ゲームクライアント Online Session のセットアップ

MatchSessionP2POnlineSession_Starter という Online Session クラスが作成され、マッチセッションのゲームクライアント実装を処理します。この Online Session クラスは必要な宣言と定義を提供するため、すぐに実装を開始できます。

MatchSessionP2POnlineSession_Starter クラスファイルは次の場所にあります。

  • Header file: /Source/AccelByteWars/TutorialModules/Play/MatchSessionP2P/MatchSessionP2POnlineSession_Starter.h
  • CPP file: /Source/AccelByteWars/TutorialModules/Play/MatchSessionP2P/MatchSessionP2POnlineSession_Starter.cpp

クラスで提供されているものを見てみましょう。この Online Session クラスの親として USessionEssentialsOnlineSession を使用しているため、Introduction to Session モジュールのすべての関数にアクセスできることに注意してください。

  • MatchSessionP2POnlineSession_Starter Header ファイルには、名前に QueryUserInfo を含む関数と変数が表示されます。チュートリアルの目的では、これらの関数と変数は無視してください。これらはマッチセッションの実装には必要ありませんが、Byte Wars には必要です。Byte Wars はこれらを使用して、サーバー上のバックエンドからプレイヤーの情報を取得し、セッションオーナーのユーザー名を表示します。

    private:
    // ...
    void OnQueryUserInfoForFindSessionComplete(
    const FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo);
  • Header ファイルには、デリゲートとそのゲッターも表示されます。このデリゲートは、リクエストを行う際に UI をレスポンスコールに接続する方法になります。

    public:
    // ...
    virtual FOnServerSessionUpdateReceived* GetOnSessionServerUpdateReceivedDelegates() override
    {
    return &OnSessionServerUpdateReceivedDelegates;
    }
    // ...
    virtual FOnMatchSessionFindSessionsComplete* GetOnFindSessionsCompleteDelegates() override
    {
    return &OnFindSessionsCompleteDelegates;
    }
    private:
    // ...
    FOnServerSessionUpdateReceived OnSessionServerUpdateReceivedDelegates;
    // ...
    FOnMatchSessionFindSessionsComplete OnFindSessionsCompleteDelegates;
  • MatchSessionTemplateNameMap という TMap 変数もあります。これの値を、以前に設定したセッションテンプレート名に設定します。

    public:
    // ...
    const TMap<TPair<EGameModeNetworkType, EGameModeType>, FString> MatchSessionTemplateNameMap = {
    {{EGameModeNetworkType::P2P, EGameModeType::FFA}, "unreal-elimination-p2p"},
    {{EGameModeNetworkType::P2P, EGameModeType::TDM}, "unreal-teamdeathmatch-p2p"}
    };
    TMap<TTuple<EGameModeType, EGameStyle>, FString> MatchSessionTargetGameModeMap = {
    { { EGameModeType::FFA, EGameStyle::Zen }, "ELIMINATION-P2P-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Zen }, "TEAMDEATHMATCH-P2P-USERCREATED" },
    { { EGameModeType::FFA, EGameStyle::Frenzy }, "FRENZY-ELIMINATION-P2P-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Frenzy }, "FRENZY-TEAMDEATHMATCH-P2P-USERCREATED" }
    };
  • 後で追加する実装に使用される2つの変数があります。

    private:
    // ...
    bool bIsInSessionServer = false;
    // ...
    TSharedRef<FOnlineSessionSearch> SessionSearch = MakeShared<FOnlineSessionSearch>(FOnlineSessionSearch());
    int32 LocalUserNumSearching;

クライアントのサーバーへの移動またはホストとしての移動

P2P の場合、2種類の移動ロジックが必要です。参加するプレイヤー用のサーバーへの移動と、セッションを作成するプレイヤー用のホストとしての移動です。

  1. 関数の宣言から始めます。MatchSessionP2POnlineSession_Starter Header ファイルを開き、次の宣言を追加します。

    public:
    // ...
    virtual bool TravelToSession(const FName SessionName) override;
    protected:
    virtual void OnSessionServerUpdateReceived(FName SessionName) override;
    virtual void OnSessionServerErrorReceived(FName SessionName, const FString& Message) override;

    virtual void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result) override;
    virtual void OnCreateSessionComplete(FName SessionName, bool bSucceeded) override;
  2. MatchSessionP2POnlineSession_Starter CPP ファイルを開き、以下の実装を追加します。TravelToSession() 関数は、その名前が示すように、キャッシュされたセッション情報からサーバーの IP アドレスを取得し、そのサーバーへの移動を試みます。OnSessionServerUpdateReceivedOnSessionServerErrorReceived は、ゲームクライアントがバックエンドからサーバーに関する更新を受信したときに実行される関数です。これは P2P セッションであるため、セッションの作成が成功したら、すぐにプレイヤーをホストとして移動させる必要があります。そのため、OnCreateSessionComplete() に実装があります。

    bool UMatchSessionP2POnlineSession_Starter::TravelToSession(const FName SessionName)
    {
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    if (GetSessionType(SessionName) != EAccelByteV2SessionType::GameSession)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Not a game session"));
    return false;
    }

    // Get session info
    const FNamedOnlineSession* Session = GetSession(SessionName);
    if (!Session)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("The session is invalid"));
    return false;
    }

    const TSharedPtr<FOnlineSessionInfo> SessionInfo = Session->SessionInfo;
    if (!SessionInfo.IsValid())
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("The session info is invalid"));
    return false;
    }

    const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(SessionInfo);
    if (!AbSessionInfo.IsValid())
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("The session info is not FOnlineSessionInfoAccelByteV2"));
    return false;
    }

    // get player controller of the local owner of the user
    APlayerController* PlayerController = GetPlayerControllerByUniqueNetId(Session->LocalOwnerId);

    // if nullptr, treat as failed
    if (!PlayerController)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Can't find player controller with the session's local owner's unique ID"));
    return false;
    }

    AAccelByteWarsPlayerController* AbPlayerController = Cast<AAccelByteWarsPlayerController>(PlayerController);
    if (!AbPlayerController)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Player controller is not (derived from) AAccelByteWarsPlayerController"));
    return false;
    }

    FString ServerAddress = "";

    // If local user is not the P2P host -> connect to host
    if (!(AbSessionInfo->GetServerType() == EAccelByteV2SessionConfigurationServerType::P2P && Session->bHosting))
    {
    UE_LOG_MATCHSESSIONP2P(Log, TEXT("Host is not a P2P host, traveling to host"));
    GetABSessionInt()->GetResolvedConnectString(SessionName, ServerAddress);
    if (ServerAddress.IsEmpty())
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Can't find session's server address"));
    return false;
    }
    }
    else
    {
    UE_LOG_MATCHSESSIONP2P(Log, TEXT("Host is a P2P host, traveling as listen server"));
    ServerAddress = "MainMenu?listen";
    }

    if (!bIsInSessionServer)
    {
    AbPlayerController->DelayedClientTravel(ServerAddress, TRAVEL_Absolute);
    bIsInSessionServer = true;
    }
    else
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Already in session's server"));
    }

    return true;
    }
    void UMatchSessionP2POnlineSession_Starter::OnSessionServerUpdateReceived(FName SessionName)
    {
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    if (bLeavingSession)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("called but leave session is currently running. Canceling attempt to travel to server"))
    OnSessionServerUpdateReceivedDelegates.Broadcast(SessionName, FOnlineError(true), false);
    return;
    }

    const bool bHasClientTravelTriggered = TravelToSession(SessionName);
    OnSessionServerUpdateReceivedDelegates.Broadcast(SessionName, FOnlineError(true), bHasClientTravelTriggered);
    }
    void UMatchSessionP2POnlineSession_Starter::OnSessionServerErrorReceived(FName SessionName, const FString& Message)
    {
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    FOnlineError Error;
    Error.bSucceeded = false;
    Error.ErrorMessage = FText::FromString(Message);

    OnSessionServerUpdateReceivedDelegates.Broadcast(SessionName, Error, false);
    }
    void UMatchSessionP2POnlineSession_Starter::OnJoinSessionComplete(
    FName SessionName,
    EOnJoinSessionCompleteResult::Type Result)
    {
    Super::OnJoinSessionComplete(SessionName, Result);

    TravelToSession(SessionName);
    }
    void UMatchSessionP2POnlineSession_Starter::OnCreateSessionComplete(FName SessionName, bool bSucceeded)
    {
    Super::OnCreateSessionComplete(SessionName, bSucceeded);

    if (bSucceeded)
    {
    // attempt to travel -> P2P host will need to travel as listen server right now
    TravelToSession(SessionName);
    }
    }
  3. クライアントがサーバーから切断されたが、まだセッションに接続されている場合に何が起こるかを処理する必要があります。この場合、プレイヤーはまだセッションの一部として扱われます。これを解決するには、この切断が発生するたびに LeaveSession を呼び出すだけです。Unreal Engine の Online Session クラスによって提供される関数 HandleDisconnectInternal を使用します。MatchSessionP2POnlineSession_Starter Header ファイルに戻り、この宣言を追加します。

    protected:
    // ...
    virtual bool HandleDisconnectInternal(UWorld* World, UNetDriver* NetDriver) override;
  4. MatchSessionP2POnlineSession_Starter CPP ファイルを開き、これらの実装を追加します。

    bool UMatchSessionP2POnlineSession_Starter::HandleDisconnectInternal(UWorld* World, UNetDriver* NetDriver)
    {
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    LeaveSession(GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession));
    bIsInSessionServer = false;

    GEngine->HandleDisconnect(World, NetDriver);

    return true;
    }
  5. ホストがセッションを離れたときも処理する必要があります。この場合、HandleDisconnectInternal は呼び出されません。なぜなら、彼らがサーバーだからです。LeaveSession レスポンスコールバックを使用して bIsInSessionServer 変数をリセットします。MatchSessionP2POnlineSession_Starter Header ファイルを開き、この宣言を追加します。

    protected:
    // ...
    virtual void OnLeaveSessionComplete(FName SessionName, bool bSucceeded) override;
  6. この実装を MatchSessionP2POnlineSession_Starter CPP ファイルに追加します。

    void UMatchSessionP2POnlineSession_Starter::OnLeaveSessionComplete(FName SessionName, bool bSucceeded)
    {
    Super::OnLeaveSessionComplete(SessionName, bSucceeded);

    if (bSucceeded)
    {
    bIsInSessionServer = false;
    }
    }

マッチセッションの作成

  1. MatchSessionP2POnlineSession_Starter Header ファイルを開き、以前に設定したセッションテンプレート名に MatchSessionTemplateNameMap マップの値を設定していることを確認します。

    public:
    // ...
    const TMap<TPair<EGameModeNetworkType, EGameModeType>, FString> MatchSessionTemplateNameMap = {
    {{EGameModeNetworkType::P2P, EGameModeType::FFA}, "unreal-elimination-p2p"},
    {{EGameModeNetworkType::P2P, EGameModeType::TDM}, "unreal-teamdeathmatch-p2p"}
    };
    TMap<TTuple<EGameModeType, EGameStyle>, FString> MatchSessionTargetGameModeMap = {
    { { EGameModeType::FFA, EGameStyle::Zen }, "ELIMINATION-P2P-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Zen }, "TEAMDEATHMATCH-P2P-USERCREATED" },
    { { EGameModeType::FFA, EGameStyle::Frenzy }, "FRENZY-ELIMINATION-P2P-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Frenzy }, "FRENZY-TEAMDEATHMATCH-P2P-USERCREATED" }
    };
  2. Header ファイルで、この関数宣言を追加します。

    public:
    // ...
    virtual void CreateMatchSession(
    const int32 LocalUserNum,
    const EGameModeNetworkType NetworkType,
    const EGameModeType GameModeType, const EGameStyle GameStyle) override;
  3. MatchSessionP2POnlineSession_Starter CPP ファイルを開き、以下の実装を追加します。SessionSettingsGAME_SESSION_REQUEST_TYPE というフラグを設定していることに注意してください。これにより、FindSessions() 関数がバックエンドにそのフラグを持つセッションのみを返すように指示でき、レスポンスを手動でフィルタリングする必要がなくなります。

    void UMatchSessionP2POnlineSession_Starter::CreateMatchSession(
    const int32 LocalUserNum,
    const EGameModeNetworkType NetworkType,
    const EGameModeType GameModeType, const EGameStyle GameStyle)
    {
    FOnlineSessionSettings SessionSettings;
    // Set a flag so we can request a filtered session from backend
    SessionSettings.Set(GAME_SESSION_REQUEST_TYPE, GAME_SESSION_REQUEST_TYPE_MATCHSESSION);

    // flag to signify the server which game mode to use
    SessionSettings.Set(GAMESETUP_GameModeCode, MatchSessionTargetGameModeMap[{ GameModeType, GameStyle }]);

    // Get match session template name based on game mode type
    FString MatchTemplateName = MatchSessionTemplateNameMap[{EGameModeNetworkType::P2P, GameModeType}];

    // Override match session template name if applicable.
    if (!UTutorialModuleOnlineUtility::GetMatchSessionTemplateP2POverride().IsEmpty())
    {
    MatchTemplateName = UTutorialModuleOnlineUtility::GetMatchSessionTemplateP2POverride();
    }

    CreateSession(
    LocalUserNum,
    GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession),
    SessionSettings,
    EAccelByteV2SessionType::GameSession,
    MatchTemplateName);
    }

マッチセッションの検索

  1. MatchSessionP2POnlineSession_Starter Header ファイルを開き、以下の2つの関数宣言を追加します。これらは呼び出し関数とレスポンスコールバックになります。

    public:
    // ...
    virtual void FindSessions(
    const int32 LocalUserNum,
    const int32 MaxQueryNum,
    const bool bForce) override;
    protected:
    // ...
    virtual void OnFindSessionsComplete(bool bSucceeded) override;
  2. MatchSessionP2POnlineSession_Starter CPP ファイルを開き、以下の実装を追加します。CreateMatchSession 実装で SessionSettings にフラグを設定したため、QuerySettings として同じフラグも設定することに注意してください。また、FindSessions() を呼び出すときにクラスメンバー変数である SessionSearch を渡していることにも注意してください。FindSessions() の動作方法は、FOnlineSessionSearch 変数の参照を受け取り、参照が指す変数を更新するため、OnFindSessionsComplete にはパラメータとしてセッション情報が含まれません。

    void UMatchSessionP2POnlineSession_Starter::FindSessions(
    const int32 LocalUserNum,
    const int32 MaxQueryNum,
    const bool bForce)
    {
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    if (SessionSearch->SearchState == EOnlineAsyncTaskState::InProgress)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Currently searching"))
    return;
    }

    // check cache
    if (!bForce && MaxQueryNum <= SessionSearch->SearchResults.Num())
    {
    UE_LOG_MATCHSESSIONP2P(Log, TEXT("Cache found"))

    // return cache
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this]()
    {
    OnFindSessionsComplete(true);
    }));
    return;
    }

    SessionSearch->SearchState = EOnlineAsyncTaskState::NotStarted;
    SessionSearch->MaxSearchResults = MaxQueryNum;
    SessionSearch->SearchResults.Empty();
    LocalUserNumSearching = LocalUserNum;

    // reset
    SessionSearch->QuerySettings = FOnlineSearchSettings();

    // Request a filtered session from backend based on the flag we set on CreateSession_Caller
    SessionSearch->QuerySettings.Set(GAME_SESSION_REQUEST_TYPE, GAME_SESSION_REQUEST_TYPE_MATCHSESSION, EOnlineComparisonOp::Equals);

    if (!GetSessionInt()->FindSessions(LocalUserNum, SessionSearch))
    {
    ExecuteNextTick(FTimerDelegate::CreateWeakLambda(this, [this]()
    {
    OnFindSessionsComplete(false);
    }));
    }
    }
    void UMatchSessionP2POnlineSession_Starter::OnFindSessionsComplete(bool bSucceeded)
    {
    UE_LOG_MATCHSESSIONP2P(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    if (bSucceeded)
    {
    // Remove owned session from result if exists
    const FUniqueNetIdPtr LocalUserNetId = GetIdentityInt()->GetUniquePlayerId(LocalUserNumSearching);
    SessionSearch->SearchResults.RemoveAll([this, LocalUserNetId](const FOnlineSessionSearchResult& Element)
    {
    return CompareAccelByteUniqueId(
    FUniqueNetIdRepl(LocalUserNetId),
    FUniqueNetIdRepl(Element.Session.OwningUserId));
    });

    // Trigger immediately if the results are empty at this point.
    if (SessionSearch->SearchResults.IsEmpty())
    {
    OnFindSessionsCompleteDelegates.Broadcast({}, true);
    return;
    }

    // Get owner’s user info for queried user info.
    TArray<FUniqueNetIdRef> UserIds;
    for (const FOnlineSessionSearchResult& SearchResult : SessionSearch->SearchResults)
    {
    UserIds.AddUnique(SearchResult.Session.OwningUserId->AsShared());
    }

    // Trigger to query user info
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    {
    StartupSubsystem->QueryUserInfo(
    LocalUserNumSearching,
    UserIds,
    FOnQueryUsersInfoCompleteDelegate::CreateUObject(this, &ThisClass::OnQueryUserInfoForFindSessionComplete));
    }
    }
    else
    {
    OnFindSessionsCompleteDelegates.Broadcast({}, false);
    }
    }
  3. 実装が完了したので、OnFindSessionsComplete をデリゲートにバインドします。CPP ファイルで、RegisterOnlineDelegates に移動し、次のコードを追加します。

    void UMatchSessionP2POnlineSession_Starter::RegisterOnlineDelegates()
    {
    // ...
    // Match session delegates
    GetSessionInt()->OnFindSessionsCompleteDelegates.AddUObject(this, &ThisClass::OnFindSessionsComplete);

    SessionSearch->SearchState = EOnlineAsyncTaskState::NotStarted;
    }
  4. そのコールバックをアンバインドする方法を実装します。CPP ファイルで、ClearOnlineDelegates に移動し、次のコードを追加します。

    void UMatchSessionP2POnlineSession_Starter::ClearOnlineDelegates()
    {
    // ...
    GetSessionInt()->OnFindSessionsCompleteDelegates.RemoveAll(this);
    }

サーバーオンラインサブシステムのセットアップ

UMatchSessionP2PServerSubsystem_Starter という Game Instance Subsystem クラスが作成され、マッチセッションサーバーの実装を処理します。この Online Session クラスは必要な宣言と定義を提供するため、すぐに実装を開始できます。

UMatchSessionP2PServerSubsystem_Starter クラスファイルは次の場所にあります。

  • Header file: /Source/AccelByteWars/TutorialModules/Play/MatchSessionP2P/MatchSessionP2PServerSubsystem_Starter.h
  • CPP file: /Source/AccelByteWars/TutorialModules/Play/MatchSessionP2P/MatchSessionP2PServerSubsystem_Starter.cpp

提供されたクラスを見てみましょう。

  • UMatchSessionP2PServerSubsystem_Starter Header ファイルには、Game specific というラベルの付いた領域が表示されます。Online Session の QueryUserInfo と同様に、このコードはマッチセッションの実装には必要ありませんが、Byte Wars には必要です。このコードは、サーバーにログインしたプレイヤーが実際にセッションの一部であることを確認します。そうでない場合は、そのプレイヤーをキックします。このチュートリアルでは、このセクションは無視してください。

    protected:
    // ...
    virtual void OnAuthenticatePlayerComplete_PrePlayerSetup(APlayerController* PlayerController) override;
  • Header ファイルには MatchmakingOnlineSession という変数もあり、これは先ほど実装した Online Session へのポインタです。

    private:
    UPROPERTY()
    UMatchSessionP2POnlineSession* OnlineSession;

サーバーがセッションを受信

サーバーがバックエンドに登録した直後、セッションが専用サーバーを必要とする場合、バックエンドはそのサーバーをセッションに割り当てます。これが発生すると、サーバーは通知を受け取ります。Byte Wars では、SessionSettings のフラグを使用して、サーバーが使用するゲームモードを決定します。

  1. UMatchSessionP2PServerSubsystem_Starter Header ファイルを開き、この関数宣言を追加します。

    protected:
    // ...
    virtual void OnServerSessionReceived(FName SessionName) override;
  2. ここのコードのほとんどは Byte Wars 固有のものです。ゲームで必要になる可能性のある行は以下で強調表示されており、セッションデータ自体を取得し、セッションの作成時に設定したカスタム SessionSettings を取得する方法の例です。

    void UMatchSessionP2PServerSubsystem_Starter::OnServerSessionReceived(FName SessionName)
    {
    Super::OnServerSessionReceived(SessionName);
    UE_LOG_MATCHSESSIONP2P(Verbose, TEXT("called"))

    #pragma region "Assign game mode based on SessionTemplateName from backend"
    // Get GameMode
    const UWorld* World = GetWorld();
    if (!World)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("World is invalid"));
    return;
    }

    AGameStateBase* GameState = World->GetGameState();
    if (!GameState)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Game State is invalid"));
    return;
    }

    AAccelByteWarsGameState* AbGameState = Cast<AAccelByteWarsGameState>(GameState);
    if (!AbGameState)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Game State is not derived from AAccelByteWarsGameState"));
    return;
    }

    // Get game session
    if (OnlineSession->GetSessionType(SessionName) != EAccelByteV2SessionType::GameSession)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("Is not a game session"));
    return;
    }

    const FNamedOnlineSession* Session = OnlineSession->GetSession(SessionName);
    if (!Session)
    {
    UE_LOG_MATCHSESSIONP2P(Warning, TEXT("The session is invalid"));
    return;
    }

    // Get game related info from session
    FString RequestedGameModeCode = TEXT("");
    Session->SessionSettings.Get(GAMESETUP_GameModeCode, RequestedGameModeCode);

    // Try getting manually set game rules, if GAMESETUP_DisplayName empty, go to the next flow
    bool bIsCustomGame = false;
    if (Session->SessionSettings.Get(GAMESETUP_IsCustomGame, bIsCustomGame) && bIsCustomGame)
    {
    AbGameState->AssignCustomGameMode(&Session->SessionSettings);
    }
    // If not complete, try getting requested game mode
    else if (!RequestedGameModeCode.IsEmpty())
    {
    AbGameState->AssignGameMode(RequestedGameModeCode);
    }
    #pragma endregion

    // Query all currently registered users' info
    AuthenticatePlayer_OnRefreshSessionComplete(true);
    }
  3. その関数を Online Subsystem(OSS)デリゲートにバインドします。CPP ファイルで、Initialize に移動し、次のコードを追加します。

    void UMatchSessionP2PServerSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    // ...
    GetABSessionInt()->OnServerReceivedSessionDelegates.AddUObject(this, &ThisClass::OnServerSessionReceived);
    }
  4. 不要になったら、その関数をアンバインドします。Deinitialize に移動し、次のコードを追加します。

    void UMatchSessionP2PServerSubsystem_Starter::Deinitialize()
    {
    // ...
    GetABSessionInt()->OnServerReceivedSessionDelegates.RemoveAll(this);
    }

リソース