Skip to main content

Unreal Engine Module - Running DS on Armada - Use Online Subsystem to set up a server

Last updated on January 13, 2024

Dedicated server flow

Let's start by understanding the flow of how a game server is managed by Armada. Take a look at the graph below.

About the subsystem

In the Byte Wars module starter project, you will see a subsystem called the MultiplayerDSEssentialsSubsystem_Starter. This class provides necessary declarations and definitions, so you can begin using them to implement DS functionalities right away.

The MultiplayerDSEssentialsSubsystem_Starter class files can be found at the following:

  • Header file can be found in /Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystem_Starter.h.
  • CPP file can be found in /Source/AccelByteWars/TutorialModules/Play/MultiplayerDSEssentials/MultiplayerDSEssentialsSubsystem_Starter.cpp.

Let's take a look at what we have provided in the class.

  • In the SessionEssentialsOnlineSession_Starter class header file, you will see three variable declarations.

    private:
    bool bServerAlreadyRegister;
    bool bUnregisterServerRunning;
    FOnlineSessionV2AccelBytePtr ABSessionInt;
    • bServerAlreadyRegister: used to indicate whether we have successfully called the RegisterServer or not, to prevent the server sending unnecessary HTTP call.
    • bUnregisterServerRunning: similar as before, to prevent the server calling UnregisterServer when it has been called and is currently waiting for the response.
    • Lastly,ABSessionInt, which is our interface to the OSS itself.
  • Now, navigate to SessionEssentialsOnlineSession_Starter class CPP file, and on to the UMultiplayerDSEssentialsSubsystem_Starter::Initialize. Notice that we have implemented a way to get the interface itself. This allows you to interact with the OSS right away, using the ABSessionInt.

    void UMultiplayerDSEssentialsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    Super::Initialize(Collection);

    // TODO: Bind delegates

    ABSessionInt = StaticCastSharedPtr<FOnlineSessionV2AccelByte>(Online::GetSessionInterface());
    ensure(ABSessionInt);
    }

Server login

Your game server needs to log in to access Armada. To do this, you just need to make sure you have set up your Server SDK correctly, and your game server will automatically perform login to Armada.

Register server

After successfully logging in, your game server needs to be registered to Armada. This allows Armada to recognize and manage your game server properly.

  1. Unreal Engine have a built-in RegisterServer() function in its GameSession class. In the Byte Wars project, we have created a class of AGameSession, the AccelByteWarsGameSession, which overrides the RegisterServer() to call OnRegisterServerDelegates delegate. So, in this implementation, we will be binding our register server function to that delegate. For reference, the AccelByteWarsGameSession class CPP file is located in /Source/AccelByteWars/Core/System/AccelByteWarsGameSession.cpp.

  2. Now that you understand the basics, let's create a register server implementation using AccelByte OSS. First, open the MultiplayerDSEssentialsSubsystem_Starter class header file and declare the following function.

    private:
    void RegisterServer(FName SessionName);
  3. Still in the header file, add another function declaration that we will use as the callback when RegisterServer completed.

    private:
    void OnRegisterServerComplete(const bool bSucceeded);
  4. Next, create the function definition in the MultiplayerDSEssentialsSubsystem_Starter class CPP file. Then, add the following code to register your game server with Armada.

    void UMultiplayerDSEssentialsSubsystem_Starter::RegisterServer(FName SessionName)
    {
    UE_LOG_MultiplayerDSEssentials(Verbose, TEXT("called"))

    // safety
    if (!ABSessionInt)
    {
    UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Session interface null"))
    OnRegisterServerComplete(false);
    return;
    }
    if (!IsRunningDedicatedServer())
    {
    UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Is not DS"));
    OnRegisterServerComplete(false);
    return;
    }

    if (bServerAlreadyRegister)
    {
    UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Already registered"));
    OnRegisterServerComplete(false);
    return;
    }

    ABSessionInt->RegisterServer(SessionName, FOnRegisterServerComplete::CreateUObject(
    this, &ThisClass::OnRegisterServerComplete));
    }
  5. Create definition for the callback. We will simply call log and flag the DS as already registered so any more attempt to register will be canceled, saving HTTP call.

    void UMultiplayerDSEssentialsSubsystem_Starter::OnRegisterServerComplete(const bool bSucceeded)
    {
    UE_LOG_MultiplayerDSEssentials(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    if (bSucceeded)
    {
    bServerAlreadyRegister = true;
    }
    }
  6. Let's bind the RegisterServer to the OnRegisterServerDelegates delegate we mentioned earlier. Place it in the MultiplayerDSEssentialsSubsystem_Starter class CPP file in this predefined function.

    void UMultiplayerDSEssentialsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    ...
    AAccelByteWarsGameSession::OnRegisterServerDelegates.AddUObject(this, &ThisClass::RegisterServer);
    ...
    }
  7. Next, unbind the delegate when the MultiplayerDSEssentialsSubsystem_Starter is uninitialized. You can do it inside this predefined function.

    void UMultiplayerDSEssentialsSubsystem_Starter::Deinitialize()
    {
    ...
    AAccelByteWarsGameSession::OnRegisterServerDelegates.RemoveAll(this);
    }
  8. There you have it. Register server to Armada implementation is completed. Please compile your project and make sure there are no errors.

Unregister and shutdown server

Once the game is over, you should unregister your game server from Armada and then shut it down. This prevents your servers from becoming zombie servers (session ended, but the server still active).

  1. Just like the register server functionality, we used GameSession to unregister server. But, the built-in GameSession, the AGameSession, doesn't have unregister server or equivalent function. So, we created a new function called UnregisterServer in AccelByteWarsGameSession and triggered in AAccelByteWarsInGameGameMode::CloseGame. To implement the unregister function itself, you will need to bind the implementation to the AccelByteWarsGameSession::OnUnregisterServerDelegates delegate.

  2. It's time to implement the unregister server functionality. Open the MultiplayerDSEssentialsSubsystem_Starter class header file and add the following function declaration.

    private:
    void UnregisterServer(FName SessionName);
  3. Add one more function declaration as the callback for UnregisterServer.

    private:
    void OnUnregisterServerComplete(const bool bSucceeded);
  4. Then, create the definition for the UnregisterServer. Open the MultiplayerDSEssentialsSubsystem_Starter class CPP file and add the following code.

    void UMultiplayerDSEssentialsSubsystem_Starter::UnregisterServer(const FName SessionName)
    {
    UE_LOG_MultiplayerDSEssentials(Verbose, TEXT("called"))

    // safety
    if (!ABSessionInt)
    {
    UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Session interface null"))
    OnUnregisterServerComplete(false);
    return;
    }
    if (!IsRunningDedicatedServer())
    {
    UE_LOG_MultiplayerDSEssentials(Warning, TEXT("Is not DS"));
    OnUnregisterServerComplete(false);
    return;
    }

    ABSessionInt->UnregisterServer(SessionName, FOnUnregisterServerComplete::CreateUObject(
    this, &ThisClass::OnUnregisterServerComplete));
    bUnregisterServerRunning = true;
    }
  5. Create definition for the callback function. We will be adding a logic to close the DS upon receiving the callback.

    void UMultiplayerDSEssentialsSubsystem_Starter::OnUnregisterServerComplete(const bool bSucceeded)
    {
    UE_LOG_MultiplayerDSEssentials(Log, TEXT("succeeded: %s"), *FString(bSucceeded ? "TRUE": "FALSE"))

    bUnregisterServerRunning = false;

    FPlatformMisc::RequestExit(false);
    }
  6. Next, bind the UnregisterServer to the OnUnregisterServerDelegate delegate we mentioned earlier. Place the binding in the MultiplayerDSEssentialsSubsystem_Starter class CPP file in this predefined function.

    void UMultiplayerDSEssentialsSubsystem_Starter::Initialize(FSubsystemCollectionBase& Collection)
    {
    ...
    AAccelByteWarsGameSession::OnRegisterServerDelegates.AddUObject(this, &ThisClass::RegisterServer);
    AAccelByteWarsGameSession::OnUnregisterServerDelegates.AddUObject(this, &ThisClass::UnregisterServer);
    ...
    }
  7. Onward to the unbinding of the delegate. Just like before, we will call the unbind in the deinitialize function.

    void UMultiplayerDSEssentialsSubsystem_Starter::Deinitialize()
    {
    ...
    AAccelByteWarsGameSession::OnRegisterServerDelegates.RemoveAll(this);
    AAccelByteWarsGameSession::OnUnregisterServerDelegates.RemoveAll(this);
    }
  8. Congratulations! Your implementation to unregister and shut down the game server is completed. Please compile your project again and make sure there are no errors.

Enabling your implementation

  1. Build AccelByteWars project and open it with Unreal Editor once it's done building.

  2. In the Unreal Editor, from the Content Browser, navigates to /Content/TutorialModules/Play/MultiplayerDSEssentials/DA_MultiplayerDSEssentials.uasset and enable the Is Starter Mode Active. Save the Data Asset.

    Data Asset changes preview

  3. There you have it. Your implementation will be used the next time you run the DS.

Resources