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

サブシステムを実装する - 専用サーバーを使用した参加可能なセッション - (Unreal Engine モジュール)

Last updated on February 4, 2026

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

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

セッション閲覧フロー

このモジュールを開始する前に、セッション閲覧フローを理解してください。

ゲームクライアントのオンラインセッションをセットアップする

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

MatchSessionDSOnlineSession_Starter クラスファイルは以下の場所にあります。

  • ヘッダーファイル: /Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/MatchSessionDSOnlineSession_Starter.h
  • CPPファイル: /Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/MatchSessionDSOnlineSession_Starter.cpp

クラスで提供されている内容を確認してください。セッション入門モジュールでアクセスできたすべての関数に引き続きアクセスできることに注意してください。このオンラインセッションクラスの親として USessionEssentialsOnlineSession を使用しているためです。

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

    public:
    // ...
    virtual void DSQueryUserInfo(
    const TArray<FUniqueNetIdRef>& UserIds,
    const FOnDSQueryUsersInfoComplete& OnComplete) override;
    protected:
    // ...
    virtual void OnDSQueryUserInfoComplete(
    const FListUserDataResponse& UserInfoList,
    const FOnDSQueryUsersInfoComplete& OnComplete) override;
    private:
    // ...
    FDelegateHandle OnDSQueryUserInfoCompleteDelegateHandle;
    // ...
    void OnQueryUserInfoForFindSessionComplete(
    const ::FOnlineError& Error,
    const TArray<TSharedPtr<FUserOnlineAccountAccelByte>>& UsersInfo);
  • ヘッダーファイルには、デリゲートとそのゲッターが表示されます。このデリゲートは、リクエストを行う際に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::DS, EGameModeType::FFA}, "unreal-elimination-ds-ams"},
    {{EGameModeNetworkType::DS, EGameModeType::TDM}, "unreal-teamdeathmatch-ds-ams"}
    };
    const TMap<TPair<EGameModeType, EGameStyle>, FString> MatchSessionTargetGameModeMap = {
    { { EGameModeType::FFA, EGameStyle::Zen }, "ELIMINATION-DS-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Zen }, "TEAMDEATHMATCH-DS-USERCREATED" },
    { { EGameModeType::FFA, EGameStyle::Frenzy }, "FRENZY-ELIMINATION-DS-USERCREATED" },
    { { EGameModeType::TDM, EGameStyle::Frenzy }, "FRENZY-TEAMDEATHMATCH-DS-USERCREATED" }
    };
  • 後で追加する実装に使用される2つの変数があります。

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

クライアントからサーバーへの移動

ゲームセッションに参加した後、バックエンドはサーバーのIPアドレスをゲームクライアントに送信します。ゲームクライアントは、指定されたIPアドレスに移動する方法が必要です。

  1. 関数を宣言します。MatchSessionDSOnlineSession_Starter ヘッダーファイルを開き、以下の宣言を追加します。

    public:
    // ...
    virtual bool TravelToSession(const FName SessionName) override;
    protected:
    virtual void OnSessionServerUpdateReceived(FName SessionName) override;
    virtual void OnSessionServerErrorReceived(FName SessionName, const FString& Message) override;
  2. MatchSessionDSOnlineSession_Starter CPPファイルを開き、以下の実装を追加します。TravelToSession() 関数は、キャッシュされたセッション情報からサーバーのIPアドレスを取得し、そのサーバーへの移動を試みます。OnSessionServerUpdateReceivedOnSessionServerErrorReceived は、ゲームクライアントがバックエンドからサーバーに関する更新を受信したときに実行される関数です。

    bool UMatchSessionDSOnlineSession_Starter::TravelToSession(const FName SessionName)
    {
    UE_LOG_MATCHSESSIONDS(Verbose, TEXT("called"))

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

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

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

    const TSharedPtr<FOnlineSessionInfoAccelByteV2> AbSessionInfo = StaticCastSharedPtr<FOnlineSessionInfoAccelByteV2>(SessionInfo);
    if (!AbSessionInfo.IsValid())
    {
    UE_LOG_MATCHSESSIONDS(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_MATCHSESSIONDS(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_MATCHSESSIONDS(Warning, TEXT("Player controller is not (derived from) AAccelByteWarsPlayerController"));
    return false;
    }

    // Make sure this is not a P2P session
    if (GetABSessionInt()->IsPlayerP2PHost(GetLocalPlayerUniqueNetId(PlayerController).ToSharedRef().Get(), SessionName))
    {
    UE_LOG_MATCHSESSIONDS(Warning, TEXT("The session is a P2P session"));
    return false;
    }

    FString ServerAddress = "";
    GetABSessionInt()->GetResolvedConnectString(SessionName, ServerAddress);

    if (ServerAddress.IsEmpty())
    {
    UE_LOG_MATCHSESSIONDS(Warning, TEXT("Can't find session's server address"));
    return false;
    }

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

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

    if (bLeavingSession)
    {
    UE_LOG_MATCHSESSIONDS(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 UMatchSessionDSOnlineSession_Starter::OnSessionServerErrorReceived(FName SessionName, const FString& Message)
    {
    UE_LOG_MATCHSESSIONDS(Verbose, TEXT("called"))

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

    OnSessionServerUpdateReceivedDelegates.Broadcast(SessionName, Error, false);
    }
  3. クライアントがサーバーから切断されたが、まだセッションサービスに接続されている場合の処理が必要です。この場合、プレイヤーは引き続きセッションの一部として扱われます。この切断が発生するたびに LeaveSession を呼び出します。Unreal Engine のオンラインセッションクラスが提供する関数、HandleDisconnectInternal を使用します。MatchSessionDSOnlineSession_Starter ヘッダーファイルに戻り、この宣言を追加します。

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

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

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

    GEngine->HandleDisconnect(World, NetDriver);

    return true;
    }

マッチセッションを作成する

  1. MatchSessionDSOnlineSession_Starter ヘッダーファイルを開き、MatchSessionTemplateNameMap マップの値を、以前にセットアップしたセッションテンプレート名に設定していることを確認してください。チュートリアルとまったく同じ名前でセッションテンプレート名を作成した場合は、以下のコードをコピーするだけです。

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

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

    void UMatchSessionDSOnlineSession_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::DS, GameModeType}];


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

    #pragma region "Region Preferences"
    // include region preferences into session setting
    if(NetworkType == EGameModeNetworkType::DS)
    {
    if (const UTutorialModuleDataAsset* ModuleDataAsset = UTutorialModuleUtility::GetTutorialModuleDataAsset(
    FPrimaryAssetId{ "TutorialModule:REGIONPREFERENCES" },
    this,
    true))
    {
    const UAccelByteWarsGameInstance* GameInstance = StaticCast<UAccelByteWarsGameInstance*>(GetGameInstance());
    ensure(GameInstance);

    // Get enabled regions
    TArray<FString> EnabledRegions = {};

    if (ModuleDataAsset->IsStarterModeActive())
    {
    // Starter mode is active, use the starter subsystem
    URegionPreferencesSubsystem_Starter* RegionPreferencesSubsystem = GameInstance->GetSubsystem<URegionPreferencesSubsystem_Starter>();
    if(RegionPreferencesSubsystem != nullptr)
    {
    EnabledRegions = RegionPreferencesSubsystem->GetEnabledRegion();
    }
    }
    else
    {
    // Starter mode is not active, use the non starter subsystem.
    URegionPreferencesSubsystem* RegionPreferencesSubsystem = GameInstance->GetSubsystem<URegionPreferencesSubsystem>();
    if(RegionPreferencesSubsystem != nullptr)
    {
    EnabledRegions = RegionPreferencesSubsystem->GetEnabledRegion();
    }
    }

    if(!EnabledRegions.IsEmpty())
    {
    FOnlineSessionSettingsAccelByte::Set(SessionSettings, SETTING_GAMESESSION_REQUESTEDREGIONS, EnabledRegions);
    }
    }
    }
    #pragma endregion

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

    リージョンプリファレンスの部分は、マッチセッション機能のみを実装したい場合は必要ありません。この機能を独自のゲームに統合するためのサンプルとして使用している場合は、統合時にこの部分を削除しても構いません。

マッチセッションを検索する

  1. MatchSessionDSOnlineSession_Starter ヘッダーファイルを開き、以下の2つの関数宣言を追加します。これらは呼び出し関数と応答コールバックになります。

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

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

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

    // check cache
    if (!bForce && MaxQueryNum <= SessionSearch->SearchResults.Num())
    {
    UE_LOG_MATCHSESSIONDS(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 UMatchSessionDSOnlineSession_Starter::OnFindSessionsComplete(bool bSucceeded)
    {
    UE_LOG_MATCHSESSIONDS(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? TEXT("TRUE") : TEXT("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));
    });

    #pragma region "Region Preferences"
    // remove session that not in region preferences
    if (const UTutorialModuleDataAsset* ModuleDataAsset = UTutorialModuleUtility::GetTutorialModuleDataAsset(
    FPrimaryAssetId{ "TutorialModule:REGIONPREFERENCES" },
    this,
    true))
    {
    const UAccelByteWarsGameInstance* GameInstance = StaticCast<UAccelByteWarsGameInstance*>(GetGameInstance());
    ensure(GameInstance);

    if (ModuleDataAsset->IsStarterModeActive())
    {
    // Starter is enabled, use the starter subsystem.
    URegionPreferencesSubsystem_Starter* RegionPreferencesSubsystem = GameInstance->GetSubsystem<URegionPreferencesSubsystem_Starter>();
    if(RegionPreferencesSubsystem != nullptr)
    {
    RegionPreferencesSubsystem->FilterSessionSearch(SessionSearch);
    }
    }
    else
    {
    // Starter is not enabled, use the non starter subsystem.
    URegionPreferencesSubsystem* RegionPreferencesSubsystem = GameInstance->GetSubsystem<URegionPreferencesSubsystem>();
    if(RegionPreferencesSubsystem != nullptr)
    {
    RegionPreferencesSubsystem->FilterSessionSearch(SessionSearch);
    }
    }
    }
    #pragma endregion

    // 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 ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance()->GetSubsystem<UStartupSubsystem>())
    #else
    if (UStartupSubsystem* StartupSubsystem = GetWorld()->GetGameInstance<UStartupSubsystem>())
    #endif
    {
    StartupSubsystem->QueryUserInfo(
    LocalUserNumSearching,
    UserIds,
    FOnQueryUsersInfoCompleteDelegate::CreateUObject(this, &ThisClass::OnQueryUserInfoForFindSessionComplete));

    return;
    }
    }

    OnFindSessionsCompleteDelegates.Broadcast({}, false);
    }
    備考

    リージョンプリファレンスの部分は、マッチセッション機能のみを実装したい場合は必要ありません。この機能を独自のゲームに統合するためのサンプルとして使用している場合は、統合時にこの部分を削除しても構いません。

  3. OnFindSessionsComplete をデリゲートにバインドします。CPPファイルで、RegisterOnlineDelegates に移動し、以下のコードを追加します。

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

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

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

    ゲームクライアントのオンラインセッション実装が完了しました。

専用サーバーのオンラインサブシステムをセットアップする

マッチセッションのサーバー実装を処理するために、UMatchSessionDSServerSubsystem_Starter というゲームインスタンスサブシステムクラスが作成されています。このオンラインセッションクラスは、すぐに実装を開始できるように必要な宣言と定義を提供します。

UMatchSessionDSServerSubsystem_Starter クラスファイルは以下の場所にあります。

  • ヘッダーファイル: /Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/MatchSessionDSServerSubsystem_Starter.h
  • CPPファイル: /Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/MatchSessionDSServerSubsystem_Starter.cpp

提供されているクラスを確認してください。

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

    protected:
    // ...
    virtual void OnAuthenticatePlayerComplete_PrePlayerSetup(APlayerController* PlayerController) override;
  • ヘッダーファイルには OnlineSession という変数もあります。これは、先ほど実装したオンラインセッションへのポインタです。

    private:
    UPROPERTY()
    UMatchSessionDSOnlineSession_Starter* OnlineSession;

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

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

  1. UMatchSessionDSServerSubsystem_Starter ヘッダーファイルを開き、この関数宣言を追加します。

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

    void UMatchSessionDSServerSubsystem_Starter::OnServerSessionReceived(FName SessionName)
    {
    Super::OnServerSessionReceived(SessionName);
    UE_LOG_MATCHSESSIONDS(Verbose, TEXT("called"))

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

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

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

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

    const FNamedOnlineSession* Session = OnlineSession->GetSession(SessionName);
    if (!Session)
    {
    UE_LOG_MATCHSESSIONDS(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. その関数をオンラインサブシステム(OSS)デリゲートにバインドします。CPPファイルで、Initialize に移動し、以下のコードを追加します。

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

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

リソース